微任务由JavaScript引擎在事件循环中强制、自动、不可跳过地执行,宏任务结束后立即清空微任务队列,且中间不插入任何宏任务或渲染;浏览器渲染也必须等微任务全部执行完毕才开始。
微任务的执行时机不是靠“手动保证”,而是由 javascript 引擎在事件循环中**强制、自动、不可跳过**地执行——只要同步代码结束,微任务队列就一定会被清空,且这个过程不接受任何打断。
关键节点:宏任务一结束,立刻清空微任务队列
每个宏任务(比如初始脚本、setTimeout 回调、点击事件处理函数)执行完毕后,引擎会立即检查微任务队列。只要队列非空,就按注册顺序逐个取出执行,直到彻底清空。这个动作是事件循环的硬性规则,不依赖开发者干预。
- 哪怕你在微任务里又创建了新的微任务(例如在
Promise.then里再调用queueMicrotask),它也会被追加到当前队列尾部,并在本轮继续执行完 - 中间不会插入任何宏任务(包括
setTimeout(..., 0)、requestAnimationFrame或 UI 渲染) - 浏览器渲染也必须等微任务全部跑完才开始,所以 DOM 更新 + 微任务逻辑能天然“对齐”在渲染前
微任务队列只在固定时机被检查
引擎只在三个明确时刻主动读取并执行微任务队列:
- 一个宏任务执行完成、调用栈变空时(最常见)
- 浏览器准备进行 UI 渲染前(确保 DOM 变更和响应逻辑在画面刷新前完成)
- 某些宿主环境特定阶段结束时(如 Node.js 的 I/O 阶段完成)
没有其他入口可以触发微任务执行,也不会因性能优化而延迟或合并——它就是“同步代码之后、下个宏任务之前”的确定性窗口。
所有微任务共享同一队列,顺序严格按注册时间
Promise.then、queueMicrotask、MutationObserver 的回调都进入同一个微任务队列,引擎按先进先出(FIFO)顺序执行:
- 先注册的一定先执行,不存在优先级差异
-
queueMicrotask比Promise.resolve().then()更轻量,但执行时机完全一致 - 不推荐在微任务中递归调度自身(如
queueMicrotask(fn)里再调自己),容易导致队列无限增长、阻塞后续宏任务
它不是“更快的宏任务”,而是事件循环的内置节奏
微任务的存在意义不是提速,而是提供一个可预测、可嵌套、与渲染强同步的执行层:
- 框架(如 Vue、React)用它批量更新状态、合并 DOM 操作
- 异步流程控制(
await后续、Promise链)依赖它维持串行语义 - 想让某段逻辑“在本次 DOM 更新后、页面重绘前运行”,
queueMicrotask是唯一可靠选择










