不积跬步之Event Loop 以及宏任务 微任务
讲到
event loop
事件循环,网上最好的资源应该就是Philip Roberts的演讲《Help, I'm stuck in an event-loop》)
以及 深入事件环(In The Loop)Jake Archibald@JSconf 2018 (我放的是B站的连接,up主已经做好了翻译) 阮一峰写的JavaScript 运行机制详解:再谈Event Loop 同样也是基于 上面第一篇文章。看完以后大受启发,学习这些资源还是应该去源头上看。
我希望这篇文章可以回答以下问题:
- event loop 它的执行顺序是什么?
- 什么是宏任务和微任务以及他们的区别?
- 实现宏任务和微任务的方式都有哪些?
我们开始吧:
1.什么是事件循环 event loop 呢?
它的组成部分包括如下:
- heap 堆内存 用来存放引用类型的数据
- stack 执行栈
- WebAPIS 浏览器的api,它们决定了什么时候把宏任务放到任务队列里面
- callbackQueue 回调队列 也叫 宏任务队列
- MicroTaskQueue 微任务的任务队列
- event loop 负责手机用户事件和非用户事件,对任务进行排队以便在核实的时候执行回调。然后它执行所有处于等待中的 JavaScript 任务(宏任务),然后是微任务,然后在开始下一次循环之前执行一些必要的渲染和绘制操作。
实际上上图是在2014年的时候出的,距离我们现在微任务的场景已经略微过时。我手绘了如下的图片来说明我们现在完整的:
WechatIMG67.jpeg它的执行顺序如下:
- 一开始整个脚本作为一个宏任务执行
- 执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列
- 当前宏任务执行完出队,检查微任务列表,有则依次执行,直到全部执行完
- 执行浏览器UI线程的渲染工作
- 检查是否有Web Worker任务,有则执行
- 执行完本轮的宏任务,回到2,依此循环,直到宏任务和微任务队列都为空。
微任务会在下一轮宏任务执行之前先执行。
微任务会一直执行,直到微任务队列清空为止,这里也有可能 无限执行下去,有这种风险。
2.什么是宏任务和微任务以及他们的区别?
宏任务:
一个宏任务
就是由执行诸如从头执行一段程序、执行一个事件回调或一个interval/timeout
被触发之类的标准机制而被调度的任意 JavaScript 代码。这些都在 任务队列(task queue)
上被调度。
在以下时机,任务会被添加到任务队列:
- 一段新程序或子程序被直接执行时(比如从一个控制台,或在一个 <script> 元素中运行代码)。
- 触发了一个事件,将其回调函数添加到任务队列时。
- 执行到一个由 setTimeout() 或 setInterval() 创建的 timeout 或 interval,以致相应的回调函数被添加到任务队列时。
事件循环驱动你的代码按照这些任务排队的顺序,一个接一个地处理它们。在当前迭代轮次中,只有那些当事件循环过程开始时 已经处于任务队列中 的任务会被执行。其余的任务不得不等待到下一次迭代。
微任务
起初微任务和任务之间的差异看起来不大。它们很相似;都由位于某个队列的JavaScript
代码组成并在合适的时候运行。但是,只有在迭代开始时队列中存在的任务才会被事件循环一个接一个地运行,这和处理微任务队列是殊为不同的。
有两点关键的区别。
首先,每当一个任务存在,事件循环都会检查该任务是否正把控制权交给其他 JavaScript 代码。如若不然,事件循环就会运行微任务队列中的所有微任务。接下来微任务循环会在事件循环的每次迭代中被处理多次,包括处理完事件和其他回调之后。
其次,如果一个微任务通过调用 queueMicrotask()
, 向队列中加入了更多的微任务,则那些新加入的微任务 会早于下一个任务运行 。这是因为事件循环会持续调用微任务直至队列中没有留存的,即使是在有更多微任务持续被加入的情况下。
3.实现宏任务和微任务的方式都有哪些?
微任务 | 宏任务 |
---|---|
MutationObserver | script |
Promise.then()或catch() | setTimeout |
Promise为基础开发的其它技术,比如fetch API | setInterval |
V8的垃圾回收过程 | setImmediate |
Node独有的process.nextTick | I/O |
queueMicrotask | UI rendering |
学习的资源:
Philip Roberts的演讲《Help, I'm stuck in an event-loop》)
深入事件环(In The Loop)Jake Archibald@JSconf 2018