微任务总是在ui渲染前执行完毕;浏览器事件循环中,每个宏任务结束后立即清空微任务队列,然后才进行重排与重绘,渲染完成后才取下一个宏任务。

微任务在渲染前后的执行时机,关键不在于“渲染前后”本身,而在于浏览器是否把 UI 渲染当作一个独立的宏任务阶段——它确实是,而且这个阶段有明确的插入位置。
微任务总是在渲染开始前清空
浏览器在每次事件循环中,执行完一个宏任务(比如 setTimeout 回调)后,会按固定顺序推进:
- 先检查并立即执行所有待处理的微任务(Promise.then、queueMicrotask 等),直到队列为空;
- 然后才进入UI 渲染阶段(重排 reflow + 重绘 repaint);
- 渲染完成后,再从宏任务队列取下一个任务(如另一个 setTimeout 或用户点击事件)。
也就是说,微任务永远赶在渲染之前执行完毕。哪怕你在微任务里修改了 DOM 样式或结构,这些变更也会被“攒”到即将发生的渲染中一并呈现——你不会看到中间状态。
为什么不能在渲染“之后”插入微任务?
因为事件循环没有“渲染后立即执行微任务”的机制。渲染本身不是微任务,也不触发微任务自动入队。如果你希望在渲染完成后再做点什么,不能靠微任务“等渲染完”,而要主动借助:
- requestAnimationFrame(callback):回调在下一次重绘前执行,常用于动画帧同步;
- setTimeout(callback, 0):退到下一个宏任务,确保渲染已发生;
- MutationObserver:监听 DOM 变更后触发,但它的回调是微任务——所以它看到的是“变更已应用但尚未渲染”的状态。
注意:requestAnimationFrame 本身属于宏任务范畴(规范中归为“rendering tasks”),但它执行时机紧贴渲染,比普通 setTimeout 更精准。
实际例子:验证微任务与渲染的时序
下面这段代码能清晰体现顺序:
console.log('1');Promise.resolve().then(() => console.log('2'));
setTimeout(() => {
console.log('3');
Promise.resolve().then(() => console.log('4'));
}, 0);
console.log('5');
输出是:1 → 5 → 2 → 3 → 4。其中 2 在第一次宏任务(脚本整体)结束后立即执行;3 是新宏任务,在渲染之后才轮到;4 则是紧跟 3 的微任务,在第二次渲染前执行。
小结:记住这个链条
同步代码 → 微任务清空 → (可选)UI 渲染 → 下一个宏任务 → 微任务清空 → ……
微任务没有“渲染后”这一档;它只认准“当前宏任务结束”这个节点。想响应渲染结果,得用 requestAnimationFrame 或 setTimeout,而不是依赖微任务的时机。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











