同步代码先执行,再清空所有微任务,最后执行一个宏任务;宏任务包括script、setTimeout等,微任务包括Promise.then、queueMicrotask等,微任务在每轮宏任务结束后立即全部执行。

直接看结果:同步代码先跑完,接着清空所有微任务,最后执行一个宏任务——这个顺序就是解题钥匙。
先认清楚哪些是宏任务、哪些是微任务
别靠“谁写在前面”猜顺序,得看任务类型:
-
宏任务:整体 script 脚本、
setTimeout、setInterval、requestAnimationFrame、I/O、UI 渲染、用户事件(如 click) -
微任务:
Promise.then/catch/finally、queueMicrotask、MutationObserver(Node.js 中还有process.nextTick)
关键点:微任务不是“快一点的宏任务”,而是被引擎强制插队——只要当前宏任务结束,它就立刻全部执行,不等下一个宏任务。
执行流程不能跳步:每一步都对应真实调度
以这段经典代码为例:
console.log('start');setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('promise'));
console.log('end');
拆解过程如下:
将一个或多个视频上传至YouTube。当用户想“上传到YouTube”“发YouTube”“批量上传”“upload to YouTube”“post videos to YouTube”或发布到YouTube时使用此技能。
- 第一轮:执行同步代码 → 输出 start、end
- 遇到
setTimeout→ 回调进宏任务队列(还没到执行时机) - 遇到
Promise.then→ 回调进微任务队列 - 同步代码执行完,调用栈清空 → 立刻检查微任务队列 → 执行
promise→ 输出 promise - 微任务队列清空 → 事件循环取一个宏任务 → 执行
setTimeout回调 → 输出 timeout
嵌套和连环触发要注意“清空”特性
微任务队列不是“执行一个就停”,而是持续取、直到空。比如:
Promise.resolve().then(() => {console.log(1);
Promise.resolve().then(() => console.log(2));
});
输出一定是 1 → 2,因为第 1 个 then 执行时又生成了新微任务,它会被追加到当前微任务队列尾部,继续执行。
而宏任务没这待遇:哪怕 setTimeout 里再套一个 setTimeout,也得等下一轮事件循环才可能执行。
实际排查时盯住两个关键节点
遇到异步顺序异常,优先检查:
-
DOM 更新没及时?→ 很可能是你把修改逻辑放在宏任务里(如
setTimeout),而渲染发生在宏任务之间;换成Promise.then或queueMicrotask就能紧贴同步代码后更新 -
多个
setTimeout执行顺序反直觉?→ 不是按代码顺序,而是按“谁先到期谁先进队”。setTimeout(fn, 0)和setTimeout(fn, 3)同时存在,0ms 的一定先入队、先执行

















