event loop一篇文章足矣

event loop

浏览器内核是多线程,Javascript是单线程。

楼主之前讲解了js的异步处理,只是讲解了异步处理的方法,但是对于异步处理的原理还是不是很了解,这篇文章是对于浏览器的线程方面对JavaScript的运行机制进行分析。

1:浏览器基本

我们经常说,js的执行环境是一个单线程,会按顺序执行代码,但是JavaScript又是可以异步,这两者感觉有冲突,但是本质上,如果理解浏览器的事件循环机制(event loop),就会觉得并不冲突。

浏览器里面不仅只有解释JavaScript的引擎,还包括很多其它的引擎。

浏览器机制.png

浏览器主要包括:

  1. 用户界面:包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示的您请求的页面外,其他显示的各个部分都属于用户界面。
  2. 浏览器引擎(浏览器内核): 在用户界面和呈现引擎之间传送指令。
  3. 呈现引擎 - 负责显示请求的内容。如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。
  4. 网络 - 用于网络调用,比如 HTTP 请求。其接口与平台无关,并为所有平台提供底层实现。
  5. 用户界面后端 - 用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。
  6. JavaScript 解释器。用于解析和执行 JavaScript 代码。
  7. 数据存储。这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie。新的 HTML 规范 (HTML5) 定义了“网络数据库”,这是一个完整(但是轻便)的浏览器内数据库。

我们今天主要讨论的是浏览器引擎(浏览器内核)JavaScript 解释器(V8引擎)之间的交互和沟通。

2:浏览器引擎(浏览器内核)

浏览器内核的是一个多线程处理,它主要包含如下几个线程

  1. GUI渲染线程: 渲染页面的html元素
  2. JavaScript引擎线程: 页面的交互和dom渲染
  3. 定时触发器线程:一定时间后,来触发对应的线程
  4. 事件触发线程:当一个事件触发该线程的时候,就会把它放到js的事件队列中等待执行。常用于异步操作。
  5. 异步http线程: 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求, 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件放到 JavaScript引擎的处理队列中等待处理。

他们之间有一下的联系:

  1. JavaScript引擎和GUI引擎互斥,不能一边操作dom一边渲染页面
  2. JavaScript引擎是单线程,所有需要按照事件处理队列来处理相应的代码。
  3. JavaScript引擎有一个监听事件(monitoring process)的功能,会持续不断的检查js引擎的主线程执行栈是否为空,如果为空就会去取事件触发线程存放在事件队列中的回调函数执行。

3:js引擎执行机制

由于js的运行环境是单线程的,一些异步操作还是需要借助于浏览器这个宿主来实现。这里简单的一个图来描述js运行的时候的流程。主要运用了浏览器的js引擎线程和事件触发线程,有时候开启网络服务和定时器也会用到其他的线程。

运行流程图.png

4: micro task 和 macro task

浏览器的事件循环依靠已事件队列,但是一个进程中不止一个事件队列,大致可以分为micro taskmacro task,常见的微任务和宏任务分别包括:

micro task :

  • nextTick
  • callback
  • Promise
  • process.nextTick
  • Object.observe
  • MutationObserver

macro task:

  • setTimeout
  • setInterval
  • I/O
  • script代码块

主要部分: 事件队列在同步队列执行完后,首先会执行nextTick,等nextTick执行完成后,然后会先执行micro task, 等micro task队列空了之后,才会去执行macro task,如果中间添加了micro task加入了micro task队列,会继续去执行micro task队列,然后再回到macro task队列。js引擎存在monitoring process进程, 会不停的监听task queue

它们的细节可以参考Tasks和Microtasks

5:event loop和宏任务、微任务

一段代码块就是一个宏任务。所有一般执行代码块的时候,也就是程序执行进入主线程了,主线程会根据不同的代码再分微任务和宏任务等待主线程执行完成后,不停地循环执行。

主线程(宏任务) => 微任务 => 宏任务 => 主线程

下图是一个简易的时间循环:

event loop.png

6: 简易的event loop

console.log('start')
Promise.resolve().then((resolve) => {
  console.log(1)
}))
console.log('end')

大致流程:

  1. 这一个代码块相当于一个macro-task,进入主线程。
  2. 遇到同步的代码console.log('start')开始执行
  3. Promise是micro-task,把它的回调函数放入微任务Event Queue
  4. 遇到console.log开始执行,执行完后,这个代码块宏任务完成,js监听进程发现主线程空了,就会去寻找微任务。
  5. 去微任务Event Queue找到Primise的回调函数,执行。

图解浏览器内部执行:

运行全图.png

7:复杂的分析

这里我们通过2个复杂的代码来检验是否已经基本了解了事件循环的机制:

// 来至于谷友的一到面试题
<script>
async function async1() {
  console.log('async1 start');
  await async2();
  console.log('async1 end');
}
async function async2() {
  console.log('async2');
}
console.log('script start');
setTimeout(function() {
    console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
    console.log('promise1');
    resolve();
  }).then(function() {
    console.log('promise2');
  });
console.log('script end');
</script>   
  1. 整个代码块作为一个宏任务,进入主线程

  2. 看到函数申明但没有执行,遇到函数console.log执行,输出script start

  3. 遇到setTimeout() ,把它的回调函数放入宏任务(setTimeout1)。

    宏任务 微任务
    setTimeout1
  4. 遇到执行async1(), 进入async的执行上下文之后,遇到console.log输出async1 start

  5. 然后遇到await async2(),由于()的优先级高,所有先执行async2(),进入async2()的执行上下文。

  6. 看到console.log输出async2,之后没有返回值,结束函数,返回undefined,返回async1的执行上下文的await undefined,由于async函数使用await后得语句会被放入一个回调函数中,所以把下面的放入微任务中。

    宏任务 微任务
    setTimeout1 async1=> awati 后面的语句
  7. 结束async1,返回全局上下文,遇到Promise构造函数,里面的函数立马执行, 输出promise1, 之后的回调函数进入微任务

    宏任务 微任务
    setTimeout1 async1=> awati 后面的语句
    new Promise() => 后的then
  8. 执行完Promise(),遇到console.log,输出script end,这里一个宏任务代码块执行完毕。

  9. 在主线程执行的过程中,事件触发线程会一直监听异步事件,当异步事件处理完成后,把它的回调函数放入事件队列,等待执行。

  10. 主线程现在空闲下来后,执行事件队列中的微任务,然后继续向下执行,遇到new Promise()后面的回调函数,执行代码,输出promise2(这里2个微任务的优先级,promise高于async)。

  11. 看到async1await后面的回调函数,执行代码,输出async1 end

    宏任务 微任务
    setTimeout1
  12. 此时微任务中的队列为空,开始执行队列中的宏任务,进入一个新的代码块。遇到console.log,输出setTimeout

  13. 执行完成,最后结果为

```javascript
script start => async1 start => async2 => promise1 => script end => promise2 => async1 end => setTimeout
```

8:总结

  1. JavaScript运行环境是单线程,不管什么代码,什么框架的异步代码,都是利用宿主对象(Node,浏览器)的其他线程来通过事件循环机制实现
  2. 我们一般说的运行和执行是不一样的,JavaScript的运行环境是JavaScript解析引擎,执行环境比如node,浏览器。
  3. JavaScript解析引擎会监听事件循环机制,把异步任务放入事件队列。

链接参考:

这一次,彻底弄懂 JavaScript 执行机制

图形制作软件

从Promise来看JavaScript中的Event Loop、Tasks和Microtasks

Js 的多宿主时代

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,498评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • JavaScript 运行机制详解:再谈Event Loop 作者:阮一峰 日期:2014年10月 8日 一年前,...
    猩崽大叔阅读 704评论 0 4
  • 你亲手给我编织的梦, 又自己把它拆毁, 给她的也一样。 你告诉我你是我的超级英雄, 你说我是你的公主你的荣幸, 说...
    return_to_zero阅读 152评论 0 0
  • 可恨平生读史少,填词用典费心神。 何须借用诗三百,曲赋深情意自真。
    梦马骨刀阅读 1,313评论 0 1