为什么需要 Event Loop?
如果一个机制存在,却没有解决任何问题,那么它就毫无意义。
JavaScript 是单线程的,main thread 一次只能做一件事。
可是浏览器要同时处理:用户点击、网络请求、脚本执行、UI 渲染……
这时,就必须要有一个统一的“调度员”来协调所有任务,它就是 Event Loop。
虽然现代浏览器已经支持 Web Workers 可以开线程,但绝大多数逻辑依旧依赖 event loop 来驱动。
1、什么是 Event Loop?
很多人都听过 event loop,但标准怎么定义呢?
👉 规范 中写道:
为了协调事件、用户交互、脚本、渲染、网络请求等,用户代理必须使用 event loop。
换句话说:event loop 是浏览器的大脑,负责“调度和执行”一切。
Task & Microtask
- 一个 event loop 有 一个或多个 task queues(任务队列)。
- 每个 task 可能是事件、ajax 回调、DOM 操作、解析 HTML……
- 还有一个 microtask queue(微任务队列),比如 Promise.then / MutationObserver。
👉 小细节:规范里特别提到,microtask 在特殊情况下可能会被转移到 task queue,比如在执行过程中“spin the event loop”。
2、浏览器 Event Loop 的整体架构
你可以把浏览器里 event loop 的运行流程想象成:
- JS 引擎只负责执行代码;
- Web APIs 负责定时器、DOM 事件、网络请求等;
- 任务队列(task queue) 收集异步完成的回调;
- event loop 就像中间的“调度员”,不断检查有没有任务需要执行。
来看个例子:
console.log('Hi');
setTimeout(function cb1() {
console.log('cb1');
}, 5000);
console.log('Bye');
执行顺序:
- 首先输出
Hi; - 注册一个定时器,把回调交给 Web APIs 等待;
- 输出
Bye; - 5 秒后,Web APIs 把 cb1 放入任务队列;
- event loop 发现主线程空闲,就把 cb1 拿出来执行,输出
cb1。
3、浏览器 Event Loop 执行步骤
浏览器里每一轮循环大致分为以下步骤:
- 从 task queue 取一个任务执行(例如 setTimeout 回调)。
- 执行 microtask checkpoint(例如 Promise.then 回调)。
- 尝试渲染 UI(但不是每次循环都会渲染)。
- 检查一些浏览器相关的任务(resize、scroll、media query 等)。
- 执行 requestAnimationFrame 的回调。
- 重新计算样式和布局。
- 如果有空闲时间,执行 requestIdleCallback。
总结一句:先执行宏任务,再清空微任务,然后渲染和其他操作,循环往复。
4、Node.js 的 Event Loop
Node.js 的 event loop 和浏览器类似,但阶段划分更细。你可以把它理解为一个“分段式流水线”:
-
timers 阶段:执行 setTimeout、setInterval 回调。
- 注意:定时器的触发时间不一定精确。
-
pending callbacks 阶段:执行一些系统级错误回调,比如 TCP 错误。
-
poll 阶段:
- 如果队列不为空 → 执行所有 I/O 回调;
- 如果为空 → 检查是否有 setImmediate,有则进入下一步,否则继续等 I/O。
-
check 阶段:执行 setImmediate 回调。
-
close callbacks 阶段:执行关闭事件,比如 socket.on('close')。
👉 关键点:
setTimeout和setImmediate的先后取决于当前 event loop 所处阶段;- 这就是为什么 Node.js 里经常会有 “输出顺序题”。
5、为什么要理解 Event Loop?
因为它决定了:
- 代码执行顺序:同步、异步、微任务先后。
- 性能优化:比如知道 requestAnimationFrame 在渲染前调用,就能做更流畅的动画。
- Bug 定位:比如为什么 setTimeout 延迟了,为什么 promise 回调先执行。
一句话:理解 event loop,就是理解 JavaScript 的运行时灵魂。
6、推荐学习资源
总结
浏览器的 event loop 更偏重于 协调 UI 渲染与任务调度,
Node.js 的 event loop 更偏向于 IO 驱动的任务管理。
无论哪种环境,搞清楚 event loop,才能写出更高效、更可靠的代码。