微任务总在当前宏任务执行完毕、调用栈清空后立即一次性全部执行,且必须在下一个宏任务开始前和页面渲染之前完成;期间新产生的微任务会追加至当前队列尾部并一并执行。

微任务队列在事件循环中执行的时机非常明确:它总是在**一个宏任务执行完毕后、下一个宏任务开始前**被清空,且是**一次性全部执行完**,中间不穿插任何渲染或宏任务。
每次宏任务结束后立即执行
无论这个宏任务是页面初始脚本、setTimeout回调、用户点击事件,还是fetch响应处理,只要它的同步代码和嵌套调用全部执行完毕(即调用栈变为空),JS引擎就会立刻检查微任务队列,并把里面所有已排队的微任务按顺序执行,直到队列为空。
- 例如:同步代码里创建了3个Promise.then,它们不会等下一个定时器,而是在当前宏任务结束的瞬间全部执行
- 即使某个微任务里又新建了一个Promise.then,这个新微任务也会被加入当前轮次的微任务队列,并在本轮内继续执行(即“递归清空”)
发生在渲染之前
浏览器在准备更新UI(比如重绘、回流)前,必须先完成所有待处理的微任务。这意味着DOM变更(如Vue响应式更新、通过MutationObserver监听的变动)如果由微任务触发,用户看到的画面会包含这些变更结果,不会出现“状态已改但界面未刷新”的闪烁或延迟。
- 典型场景:Vue的nextTick内部就是基于Promise.then实现的,确保拿到的是更新后的DOM
- 注意:不是所有DOM操作都进微任务——直接赋值innerHTML是同步的;只有异步回调中的DOM读写才受此机制影响
与宏任务队列严格分离
微任务队列不属于宏任务的一部分,也不依赖于宏任务类型。它独立存在,且优先级高于宏任务:
- 宏任务队列里可能有多个setTimeout、setInterval、I/O回调等,但每取出一个执行,就必须先清空微任务队列
- 不存在“部分执行微任务就去跑另一个宏任务”的情况,必须全部执行完才进入下一宏任务
- 常见微任务:Promise.then/catch/finally、async/await暂停恢复后的后续逻辑、MutationObserver回调
不跨事件循环阶段
微任务只在当前事件循环tick内生效。Node.js和浏览器虽有细微差异(如Node中process.nextTick在某些阶段更早),但在标准浏览器环境中,微任务的触发边界始终是“宏任务结束 → 微任务执行 → 渲染 → 下一宏任务”,不会延后到下一轮事件循环开始之后。
- 错误理解:“微任务等一会儿再执行”——实际是“立刻、马上、不容打断地执行”
- 关键判断点:看调用栈是否已清空,而不是看代码写了多少行或时间过了多久










