一、事件循环的两条队列
要理解微任务,我们必须先回到 JavaScript的事件循环(Event Loop)。
它处理两种不同类型任务:
- 宏任务队列 (Macrotask):存放着独立的、较大的任务。
- 微任务队列 (Microtask):存放着需要被紧急执行的、较小的任务。
二、微任务 vs. 宏任务
| 特性 | 微任务 (Microtask) | 宏任务 (Macrotask) |
|---|---|---|
| 常见来源 | Promise.then/catch/finally、queueMicrotask()、MutationObserver、process.nextTick (Node.js) |
setTimeout、setInterval、requestAnimationFrame、I/O操作(文件、网络)、UI事件(点击、键盘) |
| 执行时机 | 在当前任务执行结束后、下一个任务开始前立即执行。 | 在上一个任务结束后,由事件循环调度,中间可能穿插渲染和微任务。 |
| 执行数量 | 在一次循环中,会全部清空。 | 在一次循环中,只会执行一个。 |
| 优先级 | 高。可以看作是当前任务的"收尾工作"。 | 低。可以看作是全新的、独立的工作。 |
| 比喻 | 去银行办业务时,填完一张表后,你需要立即去另一个窗口签字确认。这个"签字"就是微任务,必须立刻完成,才能算当前业务(宏任务)告一段落。 | 你在银行办完了一项业务(宏任务),然后重新取号,等待叫到你的号再去办另一项完全独立的业务(下一个宏任务)。 |
三、事件循环示例
typescript
// 1. 同步代码入栈执行
console.log('Script Start');
// 2. 遇到 setTimeout,将其回调函数注册到 Web API,计时器结束后,回调被放入「宏任务队列」。
setTimeout(() => {
console.log('Timeout');
}, 0);
// 3. 遇到 Promise.resolve(),其 .then() 的回调被立即放入「微任务队列」。
Promise.resolve().then(() => {
console.log('Promise');
});
// 4. 同步代码入栈执行
console.log('Script End');
解析:
- 同步代码执行完毕 :调用栈变空。输出
Script Start和Script End。 - 检查微任务队列 :发现里面有一个
() => console.log('Promise')。 - 清空微任务队列 :执行该任务。输出
Promise。 - 检查微任务队列:现在空了。
- (可能进行渲染)
- 取一个宏任务 :从宏任务队列中取出
() => console.log('Timeout')。 - 执行宏任务 :执行该任务。输出
Timeout。
流程结束。这完美解释了我们看到的输出顺序。
总结
如果你喜欢本教程,记得点赞+收藏!关注我获取更多JavaScript/TypeScript开发干货