用一个面试题搞懂 JavaScript 的执行顺序:同步任务、微任务和宏任务
在 JavaScript 中,理解代码的执行顺序对于调试和优化代码至关重要。本文将通过一段代码实例,详细讲解同步任务、微任务和宏任务的执行顺序。
不多BB,shut up and show me the CODE!
示例代码
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
new Promise((resolve) => {
console.log(3);
resolve();
}).then(() => {
console.log(4);
});
setTimeout(() => {
console.log(5);
}, 0);
new Promise((resolve) => {
console.log(6);
setTimeout(() => {
console.log(7);
resolve();
}, 0);
}).then(() => {
console.log(8);
});
前情知识提要
Promise对象
1、resolve()方法代表成功,会执行.then(func1)里的func1函数。
2、reject()方法代表失败,会执行.catch(func2)里的func2函数。
3、resolve()、reject()方法都会执行.finally(func3)里的func3函数。
4、在resolve()、reject()执行之前,.then()、.catch()、.finally()不会放进微任务队列
分析执行顺序
我们将逐步分析这段代码的执行顺序,并在每一步记录同步任务队列、微任务队列和宏任务队列的变化。
初始状态
同步任务队列: [console.log(1), setTimeout, new Promise, setTimeout, new Promise]
微任务队列: []
宏任务队列: []
执行
console.log(1)
输出: 1
同步任务队列: [setTimeout, new Promise, setTimeout, new Promise]
微任务队列: []
宏任务队列: []
执行
setTimeout(() => {
console.log(2);
}, 0);
同步任务队列: [new Promise, setTimeout, new Promise]
微任务队列: []
宏任务队列: [console.log(2)]
执行
new Promise((resolve) => {
console.log(3);
resolve();
}).then(() => {
console.log(4);
});
输出: 3
并且执行resolve(),将.then()里的
console.log(4)
放进微任务队列
同步任务队列: [setTimeout, new Promise]
微任务队列: [console.log(4)]
宏任务队列: [console.log(2)]
执行
setTimeout(() => {
console.log(5);//放进宏任务队列
}, 0);
同步任务队列: [new Promise]
微任务队列: [console.log(4)]
宏任务队列: [console.log(2), console.log(5)]
执行
new Promise((resolve) => {
console.log(6);
setTimeout(() => {
console.log(7);
resolve();
}, 0);
}).then(() => {
console.log(8);
});
输出: 6
执行
setTimeout(() => {
console.log(7);
resolve();
}, 0);
将 console.log(7)、resolve()放进宏任务队列
同步任务队列: []
微任务队列: [console.log(4)]
宏任务队列: [console.log(2), console.log(5), console.log(7), resolve()]
执行所有微任务
执行 console.log(4)
输出: 4
同步任务队列: []
微任务队列: []
宏任务队列: [console.log(2), console.log(5), console.log(7, resolve)]
执行宏任务队列
执行 console.log(2)
输出: 2
同步任务队列: []
微任务队列: []
宏任务队列: [console.log(5), console.log(7), resolve()]
执行 console.log(5)
输出: 5
同步任务队列: []
微任务队列: []
宏任务队列: [console.log(7), resolve()]
执行 console.log(7)
输出: 7
同步任务队列: []
微任务队列: []
宏任务队列: [resolve()]
执行 resolve()
输出: 7
同步任务队列: []
微任务队列: [console.log(8)]
宏任务队列: []
执行所有微任务
执行 console.log(8)
输出: 8
同步任务队列: []
微任务队列: []
宏任务队列: []
完整的执行顺序
最终的输出顺序为:
1, 3, 6, 4, 2, 5, 7, 8
结论
通过这段代码的分析,我们可以更清楚地理解 JavaScript 的事件循环机制及其对同步任务、微任务和宏任务的调度顺序。这对编写高效、可预测的代码非常重要。希望这篇文章能帮助你更好地理解 JavaScript 的执行顺序。如果你有任何问题或建议,欢迎在评论区留言。