面试题答案
一键面试微任务队列与Promise的关系
- Promise是微任务的一种典型代表:在JavaScript事件循环机制中,微任务队列用于存放需要在当前执行栈清空后,下一轮宏任务执行前优先执行的任务。Promise的
then
方法回调会被放入微任务队列。当一个Promise被解决(resolved)或被拒绝(rejected)时,通过then
或catch
注册的回调函数不会立即执行,而是被添加到微任务队列中。 - 微任务队列保证Promise回调的有序执行:由于微任务队列的特性,所有Promise产生的微任务会按照它们添加到队列的顺序依次执行。这确保了Promise链式调用中的回调函数按照预期的顺序依次执行,从而实现了异步操作的顺序控制。
代码执行顺序分析及原因
假设代码如下:
console.log('start');
setTimeout(() => {
console.log('setTimeout callback');
}, 0);
Promise.resolve()
.then(() => {
console.log('Promise 1st then');
return Promise.resolve();
})
.then(() => {
console.log('Promise 2nd then');
});
console.log('end');
- 初始执行:
- 首先,JavaScript引擎从全局执行上下文开始执行代码。打印
start
。 - 遇到
setTimeout
,它是一个宏任务,会被放到宏任务队列中。这里设置延迟为0,但它不会立即执行,而是等待合适时机被放入执行栈。 - 遇到
Promise.resolve().then(...)
,Promise.resolve()
立即解决,其then
回调被放入微任务队列。 - 打印
end
。此时全局执行栈清空。
- 首先,JavaScript引擎从全局执行上下文开始执行代码。打印
- 微任务执行阶段:
- 因为当前执行栈为空,事件循环机制开始处理微任务队列。它会依次执行微任务队列中的任务。
- 首先执行
Promise 1st then
回调,打印Promise 1st then
,并返回一个新的已解决的Promise。这个新Promise的then
回调(Promise 2nd then
)又被放入微任务队列。 - 然后执行
Promise 2nd then
回调,打印Promise 2nd then
。此时微任务队列清空。
- 宏任务执行阶段:
- 微任务队列清空后,事件循环机制从宏任务队列中取出一个宏任务放入执行栈执行。这里
setTimeout
回调是宏任务队列中唯一的任务,执行它,打印setTimeout callback
。
- 微任务队列清空后,事件循环机制从宏任务队列中取出一个宏任务放入执行栈执行。这里
所以最终的输出顺序是:start
-> end
-> Promise 1st then
-> Promise 2nd then
-> setTimeout callback
。原因是事件循环机制先执行完当前执行栈的同步代码,然后处理微任务队列,最后才处理宏任务队列。Promise的then
回调作为微任务,会在宏任务之前执行,而setTimeout
作为宏任务会在微任务执行完后才执行。