面试题答案
一键面试JavaScript事件循环工作原理
- 调用栈(Call Stack):
- JavaScript是单线程语言,调用栈用于存储函数调用。当一个函数被调用时,它的执行上下文会被压入调用栈。函数执行完毕后,其执行上下文会从调用栈中弹出。例如:
function add(a, b) {
return a + b;
}
function main() {
let result = add(2, 3);
console.log(result);
}
main();
- 在这个例子中,
main
函数被调用,其执行上下文压入调用栈,main
函数内部调用add
函数,add
函数执行上下文压入调用栈,add
函数执行完毕返回结果,其执行上下文弹出,main
函数继续执行,最后main
函数执行完毕,其执行上下文弹出。
-
任务队列(Task Queue):
- 异步任务(如定时器、Promise等)不会立即进入调用栈执行。当异步任务有了结果(例如定时器到时间、Promise状态改变),相关的回调函数会被放入任务队列。
- 任务队列又分为宏任务队列(Macro - Task Queue)和微任务队列(Micro - Task Queue)。常见的宏任务有
setTimeout
、setInterval
、setImmediate
(Node.js环境)、I/O
、UI渲染
等;常见的微任务有Promise.then
、process.nextTick
(Node.js环境)等。
-
事件循环过程:
- 事件循环不断地检查调用栈是否为空。当调用栈为空时,事件循环会从微任务队列中取出任务执行,直到微任务队列为空。然后从宏任务队列中取出一个宏任务执行,执行完毕后,再次检查微任务队列并执行其中的任务,如此循环。
协调异步任务与同步任务的执行顺序
- 示例1:定时器与同步任务
console.log('start');
setTimeout(() => {
console.log('setTimeout callback');
}, 0);
console.log('end');
- 首先,
console.log('start')
和console.log('end')
是同步任务,会依次进入调用栈执行并打印。 setTimeout
是异步任务,它的回调函数会在0毫秒(实际是在当前调用栈清空且微任务队列清空后)被放入宏任务队列。- 当调用栈为空时,事件循环从宏任务队列中取出
setTimeout
的回调函数执行,打印setTimeout callback
。所以输出顺序是start
,end
,setTimeout callback
。
- 示例2:Promise与同步任务
console.log('start');
Promise.resolve().then(() => {
console.log('Promise then callback');
});
console.log('end');
console.log('start')
和console.log('end')
是同步任务,依次进入调用栈执行并打印。Promise.resolve().then
的回调函数会被放入微任务队列。- 当调用栈为空时,事件循环先执行微任务队列中的任务,所以会打印
Promise then callback
。输出顺序是start
,end
,Promise then callback
。
- 示例3:结合多种异步任务
console.log('start');
setTimeout(() => {
console.log('setTimeout callback');
Promise.resolve().then(() => {
console.log('Promise in setTimeout then callback');
});
}, 0);
Promise.resolve().then(() => {
console.log('Promise then callback');
});
console.log('end');
- 首先
console.log('start')
和console.log('end')
同步执行并打印。 Promise.resolve().then
的回调函数放入微任务队列,setTimeout
的回调函数放入宏任务队列。- 调用栈为空后,先执行微任务队列中的
Promise then callback
。 - 微任务队列清空后,从宏任务队列中取出
setTimeout
的回调函数执行,打印setTimeout callback
,其内部的Promise.resolve().then
回调函数又放入微任务队列。 - 再次清空微任务队列,执行
Promise in setTimeout then callback
。输出顺序为start
,end
,Promise then callback
,setTimeout callback
,Promise in setTimeout then callback
。