面试高频题,但很多人只是背了结论。用一个例子把整条链路串起来:
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
输出是 1 4 3 2,不是 1 2 3 4。
执行顺序的本质
JS 是单线程的,靠事件循环处理异步。每轮循环:
- 执行同步代码(调用栈里的)
- 清空微任务队列(microtask):
Promise.then、queueMicrotask、MutationObserver - 取一个宏任务(macrotask)执行:
setTimeout、setInterval、IO、UI 渲染 - 回到第 2 步
关键:微任务优先级高于宏任务,且要一次性清空。
上面例子:1、4 同步打印;setTimeout 进宏任务队列;Promise.then 进微任务队列;同步代码跑完 → 清空微任务打印 3 → 下一轮取宏任务打印 2。
一个真会踩的坑
在微任务里递归创建微任务,会把事件循环饿死,页面永远卡住:
// 危险:页面直接假死,渲染永远不会执行
function loop() { Promise.resolve().then(loop); }
loop();
同样的递归用 setTimeout(loop, 0) 就没事,因为每轮只取一个宏任务,浏览器有机会插入渲染。
async/await 在哪一层
await 后面的代码等价于放进 Promise.then,属于微任务:
async function f() {
console.log('a');
await null;
console.log('b'); // 微任务
}
记住这个就够了:同步 > 微任务(全清)> 宏任务(一次一个)。
楼主 · 2026-09-28 13:21 · 浏览 4

