宏任务总在微任务之后执行,每次只取一个;所有当前微任务会在JavaScript 事件循环中,**宏任务总在微任务之后执行,且每次只取一个;而所有当前已排队的微任务,会在每个宏任务结束后被一次性清空**。这是决定异步代码实际执行顺序的根本规则。每个宏任务结束后被一次性清空。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,有额外开销,但语义更清晰(比如链式处理)queueMicrotask(() => console.log('a'));
Promise.resolve().then(() => console.log('b'));
// 输出一定是 a → b
浏览器环境里,UI 渲染是一个隐式宏任务,发生在「上一个宏任务结束 → 微任务清空 → 下一个宏任务开始」之间,但它不排队、不可控、也不保证每次都发生。
setTimeout 或 requestAnimationFrame 精确控制渲染时机requestAnimationFrame 是宏任务,但被浏览器优化为“下一帧前执行”,优先级高于普通 setTimeout
queueMicrotask)会阻塞渲染,导致界面卡顿Promise.then 里又注册了新的微任务,它也会在本轮就被执行,而不是等到下一轮宏任务之后。这既是能力,也是陷阱——递归调用 queueMicrotask 不加退出条件,会直接栈溢出或阻塞主线程。