为什么 `Promise.then` 总比 `setTimeout(..., 0)` 快?微任务的秘密

一、事件循环的两条队列

要理解微任务,我们必须先回到 JavaScript的事件循环(Event Loop)

它处理两种不同类型任务:

  1. 宏任务队列 (Macrotask):存放着独立的、较大的任务。
  2. 微任务队列 (Microtask):存放着需要被紧急执行的、较小的任务。

二、微任务 vs. 宏任务

特性 微任务 (Microtask) 宏任务 (Macrotask)
常见来源 Promise.then/catch/finallyqueueMicrotask()MutationObserverprocess.nextTick (Node.js) setTimeoutsetIntervalrequestAnimationFrame、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');

解析:

  1. 同步代码执行完毕 :调用栈变空。输出 Script StartScript End
  2. 检查微任务队列 :发现里面有一个 () => console.log('Promise')
  3. 清空微任务队列 :执行该任务。输出 Promise
  4. 检查微任务队列:现在空了。
  5. (可能进行渲染)
  6. 取一个宏任务 :从宏任务队列中取出 () => console.log('Timeout')
  7. 执行宏任务 :执行该任务。输出 Timeout

流程结束。这完美解释了我们看到的输出顺序。

总结

如果你喜欢本教程,记得点赞+收藏!关注我获取更多JavaScript/TypeScript开发干货

相关推荐
benbenAItalk4 小时前
数字人口播视频的批量生产实践:素材规范、任务编排与质量验收
java·前端·音视频
2601_962885724 小时前
如何用 Python 扫描 A 股跳空缺口并统计缺口回补概率?
java·前端·python
西瓜太郎12345 小时前
Claude Code、Codex CLI、Gemini CLI 能否共用一枚 Key?先看协议选择矩阵
前端·api 网关·claude code·gemini cli·codex cli
多加点辣也没关系5 小时前
JavaScript|第31章:表单与控件
开发语言·javascript·ecmascript
陈随易7 小时前
在Finch用了62亿词元,我认为这是新一代Agent工具之神
前端·人工智能·后端
水域安全老周7 小时前
水趣钓鱼救生衣专利拆解:两级锁紧如何解决落水人衣分离
java·前端·网络
计算机魔术师8 小时前
Anthropic CEO突然喊踩刹车,OpenAI罕见力挺:AI这辆车不能只踩油门了
前端
wing988 小时前
从codex转战workbuddy使用一周的感受
前端·人工智能·后端
Highcharts.js8 小时前
常见报错排雷指南2:导出失败的官方解法
javascript·react.js·ecmascript·highcharts·可视化图表·导出模块失败·导出服务
EatFan8 小时前
Java接入支付宝 JSAPI 支付保姆教程(二):流程讲解与前后端代码讲解
前端·spring boot·后端·微信小程序·小程序·uni-app