简介
我们在上一篇 《浅析 JS 中的EventLoop 事件循环》 中提到一个 Event Queue
,其实在事件循环中 queue 一共有两种,还有一种叫 Job Queue
其中
Event Queue
在 HTML 规范中被称为Task Queue
,但是为了区分,一般都叫作Macrotask Queue
Job Queue
是在 ECMAScript 规范中谈及处理 Promise 回调时提到的,但是由于和 V8 中的实现比较相似,所以一般都称为Microtask Queue
Macrotask
Macrotasks 包含了解析 HTML、生成 DOM、执行主线程 JS 代码和其他事件如 页面加载、输入、网络事件、定时器事件等。从浏览器的角度,Macrotask 代表的是一些离散的独立的工作。
常见应用setTimeout
, setInterval
, setImmediate
, requestAnimationFrame
, I/O
, UI rendering
Microtask
Microtasks 则是为了完成一些更新应用程序状态的较小的任务,如处理 Promise 的回调和 DOM 的修改,以便让这些任务在浏览器重新渲染之前执行。Microtask 应该以异步的方式尽快执行,所以它们的开销比 Macrotask 要小,并且可以使我们在 UI 重新渲染之前执行,避免了不必要的 UI 渲染。
常见应用process.nextTick
, Promises
, Object.observe
, MutationObserver
执行顺序
Event Loop 的实现需要至少一个 Macrotask Queue 和至少一个 Microtask Queue。为了便于理解,我们都简化成一个。
简单来说,Microtask Queue 具有更高的优先级,即执行一个 Macrotask 任务后,就会清空整个 Microtask Queue,此时如果有新的 Microtask 加入也会被执行。
所以我们来看下面的代码:
执行顺序是什么?
我们已经知道 setTimeout 是 Macrotask,Promise 是 Microtask,而这段代码从上到下执行也是一个 Macrotask
步骤:
- 开始执行,执行脚本作为一个任务进入 Macrotask Queue,同时进入调用栈执行
- Line 1, 输出
script start
- Line 3 的 setTimeout 回调进入 Macrotask Queue 等待
- Line 7 的回调进入 Microtask Queue 等待
- Line 13 输出
script end
,此时脚本执行完成(即完成了一个 Macrotask) - 开始执行 Microtask Queue,从中拿出一个放入调用栈执行
- 开始执行 Line 7 的回调,该回调输出
promise1
,返回 undefined - Line 9 的回调进入 Microtask Queue,由于 Microtask Queue 没有清空,直接执行该回调,输出
promise2
,该回调返回 undefined - Microtask Queue 已清空(此时浏览器可以更新渲染UI),开始将 Macrotask Queue 中任务放入调用栈执行
- 执行 Line 3 的回调,输出
setTimeout
,Macrotask Queue 清空 - 程序执行完成
所以打印顺序为:
script start -> script end -> promise1 -> promise2 -> setTimeout
PS. 上面的这段代码执行流程,建议看原文的倒数第二篇参考文章,有动态交互操作可以演练。
总结
- Microtask 相比 Macrotask 具有更高的优先级
- Macrotask 总是在 JS 代码执行完成并且 Microtask Queue 清空之后执行
- JS 代码执行本身也是一个 Macrotask
- Microtask Queue 清空后有可能会重新渲染 UI
- Promise 属于 Microtask,setTimeout 属于 Macrotask
总体的执行顺序为:常规代码
-> promises
-> events 和 setTimeout 等
参考文章
原文链接
ECMA262 Job Queues
HTML Standard Task Queue
HTML系列:macrotask和microtask
microtask and macrotask a hands on approach
difference-between-microtask-and-macrotask-within-an-event-loop-context
公众号:码力全开
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。