浅析 JS 事件循环之 Microtask 和 Macrotask
in 码农技术宅 with 3 comments

浅析 JS 事件循环之 Microtask 和 Macrotask

in 码农技术宅 with 3 comments

简介

我们在上一篇 《浅析 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 加入也会被执行。

所以我们来看下面的代码:
microtask-code.png

执行顺序是什么?
我们已经知道 setTimeout 是 Macrotask,Promise 是 Microtask,而这段代码从上到下执行也是一个 Macrotask

步骤:

  1. 开始执行,执行脚本作为一个任务进入 Macrotask Queue,同时进入调用栈执行
  2. Line 1, 输出 script start
  3. Line 3 的 setTimeout 回调进入 Macrotask Queue 等待
  4. Line 7 的回调进入 Microtask Queue 等待
  5. Line 13 输出 script end,此时脚本执行完成(即完成了一个 Macrotask)
  6. 开始执行 Microtask Queue,从中拿出一个放入调用栈执行
  7. 开始执行 Line 7 的回调,该回调输出 promise1,返回 undefined
  8. Line 9 的回调进入 Microtask Queue,由于 Microtask Queue 没有清空,直接执行该回调,输出 promise2,该回调返回 undefined
  9. Microtask Queue 已清空(此时浏览器可以更新渲染UI),开始将 Macrotask Queue 中任务放入调用栈执行
  10. 执行 Line 3 的回调,输出 setTimeout,Macrotask Queue 清空
  11. 程序执行完成

所以打印顺序为:

script start -> script end -> promise1 -> promise2 -> setTimeout

PS. 上面的这段代码执行流程,建议看原文的倒数第二篇参考文章,有动态交互操作可以演练。

总结

microtask-macrotask.png

  1. Microtask 相比 Macrotask 具有更高的优先级
  2. Macrotask 总是在 JS 代码执行完成并且 Microtask Queue 清空之后执行
  3. JS 代码执行本身也是一个 Macrotask
  4. Microtask Queue 清空后有可能会重新渲染 UI
  5. 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

Responses / Cancel Reply
  1. 兄弟,正确的结论是先执行宏任务,再执行微任务,虽然看起来第一次先执行了Microtask。那是因为第一次Macrotask被放入Macrotask Queue了没有在主线程,到了下一次循环依然是先执行Macrotask

    Reply
  2. 小蓝鲨

    代码贴错了吧

    Reply
    1. @小蓝鲨

      感谢提醒,更新了哈~

      Reply
京ICP备15030655号-1