目录
- 引言
- 1.1 什么是事件循环?
- 1.2 为什么需要事件循环?
- 1.3 JavaScript 的单线程模型
- 事件循环的核心组件
- 2.1 调用栈(Call Stack)
- 2.2 任务队列(Task Queue)
- 2.3 微任务队列(Microtask Queue)
- 2.4 Web APIs 与宿主环境
- 事件循环的工作流程
- 3.1 同步代码的执行
- 3.2 异步代码的处理
- 3.3 微任务与宏任务的优先级
- 3.4 事件循环的完整生命周期
- 浏览器与Node.js中的事件循环差异
- 4.1 浏览器的事件循环模型
- 4.2 Node.js的事件循环模型
- 4.3 常见的兼容性问题
- 事件循环的底层实现
- 5.1 V8引擎与事件循环的协作
- 5.2 libuv库在Node.js中的作用
- 5.3 宏任务与微任务的调度机制
- 异步编程模式
- 6.1 回调函数(Callback)
- 6.2 Promise与async/await
- 6.3 Generator函数与协程
- 6.4 Web Workers与多线程
- 事件循环的性能优化
- 7.1 避免阻塞主线程
- 7.2 长任务(Long Tasks)的拆分
- 7.3 合理使用微任务
- 7.4 内存泄漏与事件监听的解绑
- 常见问题与解决方案
- 8.1 事件循环中的死锁
- 8.2 优先级反转问题
- 8.3 setTimeout的延迟不确定性
- 8.4 微任务嵌套导致的性能问题
- 调试与分析工具
- 9.1 Chrome DevTools的性能面板
- 9.2 Node.js的async_hooks模块
- 9.3 可视化事件循环的工具
- 总结与最佳实践
- 10.1 事件循环的设计哲学
- 10.2 异步编程的未来趋势
1. 引言
1.1 什么是事件循环?
事件循环(Event Loop)是 JavaScript 运行时环境的核心机制,负责协调代码执行、用户交互、网络请求等任务的调度。其核心目标是实现 非阻塞 I/O 和 异步编程,使得单线程的 JavaScript 能够高效处理并发操作。
1.2 为什么需要事件循环?
- 单线程限制:JavaScript 设计之初主要用于浏览器脚本,单线程模型避免了多线程的复杂性(如竞态条件、死锁)。
- 高响应性:通过异步处理 I/O 操作(如网络请求、文件读写),确保主线程不被长时间阻塞。
- 事件驱动架构:适应 GUI 程序和服务器端高并发场景,通过事件监听与回调机制实现高效资源利用。
1.3 JavaScript 的单线程模型
- 主线程:负责执行同步代码、DOM 操作、UI 渲染。
- Web Workers:通过创建子线程处理 CPU 密集型任务,但无法直接操作 DOM。
- 协作式调度:通过事件循环将任务分解为可中断的单元,避免独占式运行。
2. 事件循环的核心组件
2.1 调用栈(Call Stack)
- 后进先出(LIFO) 结构,用于追踪函数调用。
- 示例:
function a() {
b(); }
function b() {
c(); }
function c() {
console.log("Stack Trace"); }
a(); // 调用栈顺序:a → b → c → console.log
2.2 任务队列(Task Queue)
- 宏任务(Macrotasks): 包括
setTimeout
、setInterval
、DOM 事件、I/O 操作。 - 先进先出(FIFO):按任务到达顺序执行。
2.3 微任务队列(Microtask Queue)
- 微任务(Microtasks):包括
Promise.then
、MutationObserver
、queueMicrotask
。 - 优先级高于宏任务:每次事件循环的末尾会清空微任务队列。
2.4 Web APIs 与宿主环境
- 浏览器环境:提供
XMLHttpRequest
、fetch
、setTimeout
等异步 API。 - Node.js 环境:通过
libuv
库实现文件系统、网络等异步操作。
3. 事件循环的工作流程
3.1 同步代码的执行
console.log("Start"