事件循环
前端的 Event Loop(事件循环) 是 JavaScript 实现异步编程的核心机制。虽然 JavaScript 本身是单线程语言,但通过 Event Loop,它能够高效地处理异步任务(如定时器、网络请求、用户交互等),而不会阻塞主线程。
一、为什么需要 Event Loop?
- JavaScript 运行在浏览器的主线程(或 Node.js 的主事件循环线程)。
- 如果所有代码都同步执行,遇到耗时操作(如
setTimeout、fetch、click事件),页面就会“卡死”。 - 为了解决这个问题,浏览器提供了异步任务队列 + 事件循环机制,让 JS 能“非阻塞”地处理异步逻辑。
✅ 核心思想:同步代码立即执行,异步代码稍后执行(由 Event Loop 调度)。
二、Event Loop 的核心组成
Event Loop 依赖两个关键队列:
1. 宏任务队列(Macrotask Queue)
也叫 Task Queue,包含:
script(整体代码块)setTimeout/setIntervalsetImmediate(Node.js 独有)- I/O 操作(如 DOM 事件、AJAX 回调)
- UI 渲染(部分浏览器将其视为宏任务)
2. 微任务队列(Microtask Queue)
优先级更高,包含:
Promise.then()/.catch()/.finally()queueMicrotask()MutationObserver(监听 DOM 变化)process.nextTick()(Node.js,优先级甚至高于 Promise)
📌 关键规则:
每执行完一个宏任务,就清空当前所有微任务,然后再进行下一轮宏任务。
三、Event Loop 执行流程(简化版)
1. 执行全局 script(第一个宏任务)
↓
2. 遇到异步操作:
- 宏任务(如 setTimeout)→ 放入宏任务队列
- 微任务(如 Promise.then)→ 放入微任务队列
↓
3. 当前宏任务执行完毕
↓
4. **立即执行所有微任务**(直到微任务队列为空)
↓
5. (可选)浏览器进行 UI 渲染(如 Layout/Paint)
↓
6. 从宏任务队列取出下一个宏任务,重复上述过程
⚠️ 注意:微任务是在当前宏任务结束后、下一个宏任务开始前执行的,且会“冒泡式”执行(微任务中产生的新微任务也会被立即执行)。
四、经典示例解析
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
执行过程:
- 全局 script(宏任务)开始执行:
- 输出
'1' setTimeout→ 回调放入宏任务队列Promise.then→ 回调放入微任务队列- 输出
'4'
- 输出
- 当前宏任务结束 → 执行所有微任务 → 输出
'3' - 下一轮 Event Loop → 执行宏任务 → 输出
'2'
✅ 最终输出:
1
4
3
2
五、总结一句话
Event Loop 是浏览器协调同步代码、异步回调、渲染更新的调度机制:它先执行一个宏任务,再清空所有微任务,如此循环往复,确保页面响应性和异步逻辑的正确执行。
掌握 Event Loop,是理解前端性能优化、动画调度、框架原理(如 React/Vue 的更新机制)的基础。