js实现eventloop
Event Loop 的基本概念
Event Loop 是 JavaScript 运行时环境的核心机制,用于处理异步任务和执行顺序。它确保非阻塞 I/O 操作能够高效执行,同时保持单线程的特性。
调用栈(Call Stack)
调用栈是 JavaScript 执行同步代码的地方。函数调用会被压入栈中,执行完毕后弹出。如果栈中出现长时间运行的任务,会导致阻塞。
function foo() {
console.log('foo');
}
function bar() {
foo();
console.log('bar');
}
bar();
// 输出顺序: foo -> bar
任务队列(Task Queue)
异步操作(如 setTimeout、setInterval、I/O 回调)完成后,会将回调函数放入任务队列。Event Loop 在调用栈为空时,从任务队列中取出任务执行。

console.log('Start');
setTimeout(() => console.log('Timeout'), 0);
console.log('End');
// 输出顺序: Start -> End -> Timeout
微任务队列(Microtask Queue)
微任务队列优先级高于任务队列,通常包括 Promise 的回调、MutationObserver 等。每次调用栈清空后,Event Loop 会先处理所有微任务,再处理任务队列。
console.log('Start');
Promise.resolve().then(() => console.log('Promise'));
setTimeout(() => console.log('Timeout'), 0);
console.log('End');
// 输出顺序: Start -> End -> Promise -> Timeout
Event Loop 的执行流程
- 执行同步代码,填充调用栈。
- 遇到异步任务时,将回调注册到对应队列(宏任务或微任务)。
- 调用栈清空后,检查微任务队列并执行所有微任务。
- 执行一个宏任务(如
setTimeout回调)。 - 重复步骤 3-4,直到所有队列为空。
代码示例分析
console.log('Script start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve()
.then(() => {
console.log('Promise 1');
})
.then(() => {
console.log('Promise 2');
});
console.log('Script end');
输出顺序:

Script startScript endPromise 1Promise 2setTimeout
常见异步操作分类
- 宏任务(Macrotasks):
setTimeout,setInterval,I/O,UI rendering - 微任务(Microtasks):
Promise,process.nextTick(Node.js),MutationObserver
Node.js 中的 Event Loop
Node.js 的 Event Loop 分为多个阶段,每个阶段处理特定类型的任务:
- Timers: 执行
setTimeout和setInterval回调。 - I/O Callbacks: 执行大多数 I/O 相关的回调。
- Idle/Prepare: 内部使用。
- Poll: 检索新的 I/O 事件。
- Check: 执行
setImmediate回调。 - Close Callbacks: 执行关闭事件的回调(如
socket.on('close'))。
setImmediate(() => console.log('setImmediate'));
setTimeout(() => console.log('setTimeout'), 0);
// 输出顺序可能交替,取决于上下文
避免阻塞 Event Loop
长时间运行的同步代码会阻塞 Event Loop,导致异步任务无法及时处理。解决方案包括:
- 将计算密集型任务拆分为小块,使用
setTimeout或setImmediate分步执行。 - 使用 Web Workers 或子进程(Node.js)处理耗时任务。
// 错误示例:阻塞 Event Loop
for (let i = 0; i < 1e9; i++) { /* 长时间运算 */ }
// 改进:分块执行
function chunkedTask(start, end) {
if (start >= end) return;
setTimeout(() => {
for (let i = start; i < Math.min(start + 1e6, end); i++) { /* 处理小块 */ }
chunkedTask(start + 1e6, end);
}, 0);
}
总结
Event Loop 通过调用栈、任务队列和微任务队列的协作,实现了 JavaScript 的异步非阻塞执行。理解其运行机制有助于编写高效的异步代码,避免常见性能问题。






