JavaScript 的事件循环(Event Loop)是处理异步操作的机制。它允许 JavaScript 在单线程环境中执行异步代码,而不会阻塞主线程。以下是事件循环的工作原理和关键概念。
一、事件循环的工作原理
- 调用栈(Call Stack):调用栈是一个 LIFO(后进先出)结构,用于存储函数调用。当函数执行时,它会被推入调用栈;当函数执行完毕时,它会从调用栈中弹出。
- 任务队列(Task Queue):任务队列是一个 FIFO(先进先出)结构,用于存储异步任务(如回调函数、事件处理程序等)。当调用栈为空时,事件循环会从任务队列中取出第一个任务并将其推入调用栈执行。
- 微任务队列(Microtask Queue):微任务队列也是一个 FIFO 结构,用于存储微任务(如 Promise 的回调函数)。微任务的优先级高于宏任务,当一个宏任务执行完毕后,事件循环会先处理所有的微任务,然后再处理下一个宏任务。
二、事件循环的步骤
- 执行同步代码:从调用栈中执行所有同步代码,直到调用栈为空。
- 执行微任务:从微任务队列中取出所有微任务并执行,直到微任务队列为空。
- 执行宏任务:从任务队列中取出第一个宏任务并执行。
- 重复步骤 2 和 3:事件循环会不断重复上述步骤,直到所有任务都执行完毕。
三、示例
以下是一个简单的示例,展示了事件循环的工作原理:
console.log('Start');
setTimeout(() => {
console.log('Timeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise');
});
console.log('End');
执行顺序
- 同步代码:首先执行同步代码,输出 Start 和 End。
- 微任务:然后执行微任务,输出 Promise。
- 宏任务:最后执行宏任务,输出 Timeout。
输出结果:
Start
End
Promise
Timeout
事件循环中的执行顺序
- 同步代码:首先执行同步代码,初始化 abort_promise 和 promise。
- 异步操作:如果设置了 timeout,setTimeout 定时器会在指定时间后将 abort 方法推入任务队列。
- 微任务:如果 fetch 请求完成,Promise.race 会将其回调推入微任务队列。
- 宏任务:当 setTimeout 定时器触发时,abort 方法会被执行,拒绝 abort_promise。
四、总结
JavaScript 的事件循环是处理异步操作的核心机制。通过调用栈、任务队列和微任务队列,事件循环能够在单线程环境中高效地执行异步代码。理解事件循环的工作原理有助于编写高性能和响应迅速的 JavaScript 应用程序。