彻底搞懂 前端宏任务 微任务 await promise setTimeout的执行顺序
先看一道题,先自己看是否能做出来,有一定的难度哦。 正确答案最后揭晓。 先给出做出这道题所需要用到的知识点: 如果 fn() 是同步的:后续代码立即进微任务队列。 最后只需要记住一个中级规则,是先执行微任务,在执行宏任务,执行完一个宏任务,必须要清空他所产生的所有微任务console.log('1');
// 宏任务 A
setTimeout(() => {
console.log('2');
Promise.resolve().then(() => {
console.log('3');
});
}, 0);
async function task1() {
console.log('4');
await task2();
console.log('5');
}
async function task2() {
console.log('6');
// 关键点:返回一个尚未 resolve 的 Promise
return new Promise((resolve) => {
// 宏任务 B
setTimeout(() => {
console.log('7');
resolve();
}, 0);
});
}
task1();
new Promise((resolve) => {
console.log('8');
resolve();
}).then(() => {
// 微任务 1
console.log('9');
// 宏任务 C
setTimeout(() => {
console.log('10');
}, 0);
});
console.log('11');
同步代码:是第一个宏任务,永远拥有最高执行权。
微任务 (Promise.then/await):只要被触发(Resolve),就立即进入微任务队列,在当前宏任务结束前清空。
宏任务 (setTimeout):被调用时只是在排队,必须等前面的宏任务及其产生的微任务全部处理完,才轮到下一个。
这是最容易犯错的地方。记住你的通关公式:await fn();
console.log('后续');
// 等价于
fn().then(() => {
console.log('后续');
});
如果 fn() 包含异步(如 setTimeout):后续代码必须等那个异步操作执行完并调用 resolve 后,才会被推入微任务队列。
宏任务之间比的是 “谁先到达计时终点”(在 0ms 延迟下,就是比谁的代码先被执行到)。
即便一个宏任务是在微任务里产生的(比如题中的 10),它也必须排在已经存在的宏任务队列末尾。
宏任务 A -> 执行 A -> 清空 A 产生的微任务 -> 渲染 UI -> 执行下一个宏任务 B。
如果微任务里又产生了宏任务,它不会“插队”,而是去宏任务队列最后排队。
比如这个 3 就是紧跟2 后面的// 宏任务 A
setTimeout(() => {
console.log('2');
Promise.resolve().then(() => {
console.log('3');
});
}, 0);