微任务在同步代码执行完后、渲染和下一个宏任务前立即执行,按fifo清空队列;如promise.then回调输出在console.log之后、settimeout之前,保障状态一致与dom更新及时性。

这句话其实是在说:微任务不是“立刻执行”,而是“紧接在当前同步代码块执行完之后、下一个宏任务开始之前”执行——它不打断当前同步流程,但又比所有宏任务都更早落地。
微任务不是真正的“同步”,但执行时机非常靠前
同步代码(比如 console.log、变量赋值、函数调用)会逐行压入执行栈并立即运行。而微任务(如 Promise.then、MutationObserver 回调)不会插队进当前同步流程,但一旦同步代码全部跑完,JS 引擎马上回头检查微任务队列,并一口气清空它——中间不穿插任何渲染或宏任务。
- 它不阻塞当前同步执行,所以不是传统意义的“同步”
- 但它也不等下一轮事件循环,所以比
setTimeout这类宏任务快得多 - 这个“即时”,指的是时机上的紧邻性,而非语法上的同步写法
关键节点:就在同步结束、渲染之前
浏览器在每次宏任务结束后,会按固定顺序处理:
- 先跑完所有同步代码
- 然后立即执行全部待处理的微任务(FIFO,一个接一个)
- 再触发 UI 渲染(如有 DOM 变更)
- 最后才取下一个宏任务(比如
setTimeout回调)
所以像 Promise.resolve().then(() => console.log('hi')) 这样的代码,虽然写在同步语句后面,但输出一定出现在 setTimeout 之前,也一定出现在页面重绘之前。
举个直观例子
这段代码:
console.log('1');
Promise.resolve().then(() => console.log('2'));
console.log('3');
setTimeout(() => console.log('4'), 0);
输出是 1 → 3 → 2 → 4。
'2' 没有和 '1' 一起输出,说明它不是同步;但它抢在 '4' 前面,说明它在同步结束后“马上”执行了——这就是“同步代码执行后的即时操作”的真实含义。
为什么设计成这样?
主要是为了保障一致性与响应性:
- 让 Promise 链的后续逻辑能在 DOM 更新前完成,避免状态错乱
- 让 MutationObserver 能在浏览器渲染前批量处理 DOM 变化,减少重排重绘
- 避免开发者手动用
setTimeout(..., 0)模拟“下一次 tick”,提升可预测性











