宏任务执行完毕后,事件循环立即清空所有微任务(如promise.then、queuemicrotask),新产生的微任务也会追加并执行直至队列为空;随后可能触发ui渲染(浏览器环境),最后才取下一个宏任务。

宏任务执行结束后,事件循环会立即进入一个关键检查点:先清空当前所有微任务,再决定下一步动作。
微任务队列优先清空
只要当前宏任务(比如一段脚本、setTimeout 回调、I/O 完成回调)执行完毕,引擎立刻暂停宏任务调度,转向微任务队列。它会:
- 从队首开始逐个取出 Promise.then、queueMicrotask、MutationObserver 回调等微任务
- 执行过程中新产生的微任务(如在 then 里再链一个 then)也会被追加到当前队列尾部,并继续执行,直到队列彻底为空
- 这个过程不中断、不插队、不等待——微任务之间没有“下一轮”概念,是一次性全部跑完
UI 渲染可能触发
微任务清空后,浏览器会判断是否需要更新界面:
- 如果 DOM 在本轮宏任务中被修改过(例如 innerHTML 变更、class 切换),且微任务已处理完毕,通常会安排一次 UI 渲染
- 这个渲染不是强制的,而是由浏览器根据帧率、页面可见性等策略决定;Node.js 环境则无此步骤
- Vue 的 nextTick、React 的 flushSync 后续行为,都依赖这个时机点
下一个宏任务才开始取用
只有完成上述两步(微任务清空 + 可选渲染)后,事件循环才会回到宏任务队列,取出下一个待执行的宏任务:
- 可能是 setTimeout 或 setInterval 的回调
- 也可能是用户点击触发的事件处理器
- 还可能是网络请求(fetch)返回后推入的回调
- 注意:setTimeout(0) 并不等于“立刻”,它只是尽快排进下一个宏任务槽位,实际执行要等到当前轮全部收尾之后
这个检查点是理解异步顺序的核心枢纽,也是很多看似“奇怪”的输出顺序(比如 promise 总比 timeout 先打印)的根本原因。











