JavaScript 中的事件循环是一种处理异步操作的机制,它包括调用栈(call stack)、消息队列(message queue)和事件循环(event loop)。让我们详细了解这些概念,并提供一些示例。
1. 调用栈(Call Stack):
调用栈是一个数据结构,用于追踪函数的调用关系。当函数被调用时,会将该函数添加到调用栈的顶部,当函数执行完毕时,则从调用栈中移除。
function foo() {
console.log('Inside foo');
}
function bar() {
console.log('Inside bar');
foo();
}
bar();
在上面的例子中,调用栈的状态会如下:
- 初始状态:空栈
- 调用
bar()
:bar
进入栈顶 - 调用
foo()
:foo
进入栈顶 foo
执行完毕,从栈顶移除bar
执行完毕,从栈顶移除
2. 消息队列(Message Queue):
消息队列用于存储异步任务的回调函数。当异步任务完成后,相应的回调函数会被推送到消息队列中。
console.log('Start');
setTimeout(() => {
console.log('Inside setTimeout');
}, 0);
console.log('End');
在上面的例子中,setTimeout
是一个异步任务,它会在事件循环的下一个循环中将回调函数推送到消息队列中。
3. 事件循环(Event Loop):
事件循环是 JavaScript 运行时处理异步任务的机制。它会不断地检查调用栈和消息队列。当调用栈为空时,事件循环会从消息队列中取出一个任务,并将其推送到调用栈中执行。
console.log('Start');
setTimeout(() => {
console.log('Inside setTimeout');
}, 0);
console.log('End');
在上面的例子中,事件循环的步骤:
- 执行同步代码:输出 ‘Start’ 和 ‘End’
- 将
setTimeout
回调函数推送到消息队列中 - 检查调用栈,发现为空
- 从消息队列中取出
setTimeout
回调函数,推送到调用栈中执行
4. 微任务队列(Microtask Queue):
微任务队列存储微任务,它们比宏任务具有更高的优先级。微任务队列中的任务会在当前宏任务执行完毕后立即执行。
console.log('Start');
setTimeout(() => {
console.log('Inside setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('Inside Promise');
});
console.log('End');
在上面的例子中,Promise
的 then
方法中的回调函数会被推送到微任务队列中,而不是消息队列。
5. 具体执行顺序总结:
- 执行同步代码
- 将微任务推送到微任务队列
- 将宏任务推送到消息队列
- 如果调用栈为空,从微任务队列中取出任务执行
- 如果微任务队列为空,从消息队列中取出任务执行