面试题答案
一键面试-
JavaScript事件循环机制处理异步任务流程:
- 调用栈(Call Stack):JavaScript是单线程的,它通过调用栈来处理函数调用。当执行一个函数时,会将其添加到调用栈的栈顶,函数执行完毕后,从栈顶移除。
- 任务队列(Task Queue):异步任务(宏任务和微任务)在其回调函数可以执行时,会被放入相应的任务队列。宏任务队列有多个,常见的宏任务如
setTimeout
、setInterval
、setImmediate
(Node.js环境)、I/O
操作等;微任务队列只有一个,常见的微任务如Promise.then
、MutationObserver
等。 - 事件循环:事件循环不断检查调用栈是否为空。当调用栈为空时,事件循环会从微任务队列中取出任务并将其放入调用栈执行,直到微任务队列为空。然后,事件循环从宏任务队列中取出一个宏任务放入调用栈执行,执行完毕后,再次检查微任务队列,重复上述过程。
-
给定代码执行顺序分析:
- 首先,执行
console.log('script start');
,输出script start
。 - 接着遇到
setTimeout
,它是一个宏任务,其回调函数会被放入宏任务队列,等待被执行。 - 然后
Promise.resolve()
返回一个已解决的Promise,其.then
回调函数会被放入微任务队列。第一个.then
回调() => { console.log('promise1'); }
放入微任务队列,第二个.then
回调() => { console.log('promise2'); }
也放入微任务队列。 - 再执行
console.log('script end');
,输出script end
。 - 此时调用栈为空,事件循环开始处理微任务队列。从微任务队列取出第一个
.then
回调() => { console.log('promise1'); }
放入调用栈执行,输出promise1
。执行完毕后,从微任务队列取出第二个.then
回调() => { console.log('promise2'); }
放入调用栈执行,输出promise2
。微任务队列此时为空。 - 最后,事件循环从宏任务队列取出
setTimeout
的回调函数放入调用栈执行,输出setTimeout
。 - 所以执行顺序为:
script start
->script end
->promise1
->promise2
->setTimeout
。
- 首先,执行
-
在
.then
回调里添加setTimeout
后的执行顺序分析: 假设在promise1
的.then
回调里添加setTimeout
,代码如下:
console.log('script start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve()
.then(() => {
console.log('promise1');
setTimeout(() => {
console.log('new setTimeout');
}, 0);
})
.then(() => {
console.log('promise2');
});
console.log('script end');
- 首先,执行
console.log('script start');
,输出script start
。 - 接着
setTimeout
的回调函数放入宏任务队列。 Promise.resolve()
的.then
回调函数放入微任务队列,第一个.then
回调() => { console.log('promise1'); setTimeout(() => { console.log('new setTimeout'); }, 0); }
和第二个.then
回调() => { console.log('promise2'); }
放入微任务队列。- 执行
console.log('script end');
,输出script end
。 - 调用栈为空,处理微任务队列。取出第一个
.then
回调放入调用栈执行,输出promise1
,此时又创建了一个新的setTimeout
宏任务,其回调函数放入宏任务队列。 - 第一个
.then
回调执行完毕,取出第二个.then
回调放入调用栈执行,输出promise2
。微任务队列此时为空。 - 事件循环从宏任务队列取出最早放入的
setTimeout
(即一开始的那个)回调函数放入调用栈执行,输出setTimeout
。 - 宏任务队列还有一个新的
setTimeout
回调,事件循环再取出该回调放入调用栈执行,输出new setTimeout
。 - 新的执行顺序为:
script start
->script end
->promise1
->promise2
->setTimeout
->new setTimeout
。