宏任务总在微任务之后执行,每次只取一个;所有当前微任务会在每个宏任务结束后被一次性清空。Promise.then属微任务,setTimeout属宏任务,故前者总先于后者执行;queueMicrotask与Promise.then同为微任务,谁先注册谁先执行;UI渲染发生在微任务清空后、下一宏任务前,但不可控。

为什么 Promise.then 总比 setTimeout 先打印?
因为 Promise.then 是微任务,setTimeout 回调是宏任务。事件循环不会“跳过”微任务去执行下一个宏任务——哪怕那个宏任务已经等了很久。
- 同步代码(如全局脚本)本身就是一个宏任务
- 它执行完后,引擎立刻检查微任务队列,把所有
Promise.then、queueMicrotask等全部执行完 - 这时才从宏任务队列里取下一个,比如
setTimeout回调
console.log(1); setTimeout(() => console.log(2), 0); Promise.resolve().then(() => console.log(3)); console.log(4); // 输出:1 → 4 → 3 → 2
queueMicrotask 和 Promise.then 谁先执行?
它们同属微任务,进入同一微任务队列,**谁先注册谁先执行**,没有优先级差异。
-
queueMicrotask更轻量,不涉及 Promise 状态管理,适合纯调度场景 -
Promise.then会创建新 Promise,有额外开销,但语义更清晰(比如链式处理) - 两者混用时,顺序取决于调用时机,不是 API 类型
queueMicrotask(() => console.log('a'));
Promise.resolve().then(() => console.log('b'));
// 输出一定是 a → b
UI 渲染在哪个环节发生?
浏览器环境里,UI 渲染是一个隐式宏任务,发生在「上一个宏任务结束 → 微任务清空 → 下一个宏任务开始」之间,但它不排队、不可控、也不保证每次都发生。
- 你不能用
setTimeout或requestAnimationFrame精确控制渲染时机 -
requestAnimationFrame是宏任务,但被浏览器优化为“下一帧前执行”,优先级高于普通setTimeout - 频繁触发微任务(如连续
queueMicrotask)会阻塞渲染,导致界面卡顿
Promise.then 里又注册了新的微任务,它也会在本轮就被执行,而不是等到下一轮宏任务之后。这既是能力,也是陷阱——递归调用 queueMicrotask 不加退出条件,会直接栈溢出或阻塞主线程。











