js eventloop实现
JavaScript 事件循环的实现原理
JavaScript 的事件循环(Event Loop)是其异步编程的核心机制,通过单线程处理任务队列实现非阻塞执行。以下是其实现的关键组成部分和运行逻辑:
调用栈(Call Stack)
调用栈负责跟踪当前执行的函数。当函数被调用时,会被推入栈顶;执行完毕后从栈顶弹出。若栈中任务耗时过长(如同步循环),会导致页面“冻结”。
function foo() {
console.log("foo");
}
function bar() {
foo();
}
bar(); // 调用栈顺序: bar -> foo -> console.log
任务队列(Task Queues)
异步任务完成后,其回调函数被放入对应的队列中,主要分为两类:
- 宏任务队列(Macrotask Queue):包含
setTimeout、setInterval、I/O 操作、UI 渲染等。 - 微任务队列(Microtask Queue):包含
Promise.then、MutationObserver、queueMicrotask。
事件循环流程
- 执行当前调用栈中的同步代码,直到栈为空。
- 检查微任务队列,依次执行所有微任务,直到队列清空。
- 渲染页面(如有需要)。
- 从宏任务队列中取出一个任务执行,回到步骤1。
console.log("Start");
setTimeout(() => console.log("Timeout"), 0);
Promise.resolve().then(() => console.log("Promise"));
console.log("End");
// 输出顺序: Start -> End -> Promise -> Timeout
关键特性
- 微任务优先级高于宏任务:每次调用栈清空后,会先处理所有微任务。
- 避免阻塞:长时间运行的同步代码会延迟事件循环,需将耗时任务拆解为异步操作。
- Web Workers:可通过多线程分担计算密集型任务,与主线程通过
postMessage通信。
手动模拟事件循环
以下代码模拟简化版事件循环:

class EventLoop {
constructor() {
this.macrotaskQueue = [];
this.microtaskQueue = [];
}
enqueueMacrotask(task) {
this.macrotaskQueue.push(task);
}
enqueueMicrotask(task) {
this.microtaskQueue.push(task);
}
run() {
while (true) {
// 执行所有微任务
while (this.microtaskQueue.length > 0) {
const task = this.microtaskQueue.shift();
task();
}
// 执行一个宏任务
if (this.macrotaskQueue.length > 0) {
const task = this.macrotaskQueue.shift();
task();
}
}
}
}
实际应用注意事项
- 避免微任务嵌套过深:递归添加微任务可能导致无限循环。
- 宏任务拆分:长时间任务可使用
setTimeout或requestIdleCallback分片执行。 - Node.js 差异:Node.js 中
process.nextTick优先级高于微任务,且存在setImmediate等特有 API。
通过理解事件循环机制,可以更好地优化代码性能并避免常见的异步陷阱。






