js事件循环机制
JavaScript 事件循环 (Event Loop) 详解
事件循环是 JavaScript 实现异步编程的核心机制,它使得单线程的 JavaScript 能够处理非阻塞 I/O 操作。下面我将从基础概念到实际运行过程全面讲解。
一、基本组成
JavaScript 运行时包含以下关键组件:
-
调用栈 (Call Stack):记录函数调用的栈结构,后进先出
-
任务队列 (Task Queue):
-
宏任务队列 (Macrotask Queue):setTimeout、setInterval、I/O 等
-
微任务队列 (Microtask Queue):Promise、MutationObserver 等
-
-
事件循环 (Event Loop):协调调用栈和任务队列的机制
-
Web APIs:浏览器提供的异步功能(如 DOM 事件、Ajax 等)
二、运行机制
1. 同步代码执行
所有同步代码按顺序直接进入调用栈执行
javascript
复制
下载
console.log('Script start'); // 同步代码
2. 遇到异步代码
-
宏任务:交给 Web API 处理,完成后回调进入宏任务队列
-
微任务:Promise 等产生的回调进入微任务队列
javascript
复制
下载
setTimeout(() => {console.log('setTimeout'); // 宏任务 }, 0);Promise.resolve().then(() => {console.log('Promise'); // 微任务 });
3. 事件循环处理流程
-
执行当前调用栈中的所有同步代码
-
当调用栈为空时:
a. 先检查微任务队列,执行所有微任务
b. 执行一个宏任务
c. 再次检查微任务队列并执行
d. 重复此循环
三、完整示例分析
javascript
复制
下载
console.log('Script start');setTimeout(function() {console.log('setTimeout'); }, 0);Promise.resolve().then(function() {console.log('Promise 1'); }).then(function() {console.log('Promise 2'); });console.log('Script end');
执行顺序:
-
Script start
(同步) -
Script end
(同步) -
Promise 1
(微任务) -
Promise 2
(微任务) -
setTimeout
(宏任务)
四、任务优先级
-
微任务 优先于 宏任务
-
常见微任务:Promise.then、MutationObserver、process.nextTick(Node.js)
-
常见宏任务:setTimeout、setInterval、I/O、UI渲染
-
-
同类型任务按入队顺序执行
五、浏览器 vs Node.js
特性 | 浏览器环境 | Node.js 环境 |
---|---|---|
微任务类型 | Promise、MutationObserver | Promise、process.nextTick |
宏任务类型 | setTimeout、DOM事件 | setTimeout、I/O、setImmediate |
执行时机 | 每轮事件循环之间 | 各阶段之间切换时 |
六、实际应用注意事项
-
避免阻塞事件循环
-
长时间运行的同步代码会阻塞页面渲染
javascript
复制
下载
// 错误示例 function longTask() {let i = 0;while(i < 1000000000) i++; // 阻塞 }
-
-
合理使用任务队列
-
紧急任务用微任务(Promise)
-
不紧急任务用宏任务(setTimeout)
-
-
理解渲染时机
-
浏览器通常在宏任务之间执行渲染
-
大量微任务会延迟渲染
-
七、可视化理解
复制
下载
[调用栈] → [执行同步代码]↓ [遇到异步API] → [Web API处理]↓ ↓ [继续执行] [完成回调]↓ ↓ [同步完成] → [检查微任务队列] → [全部执行]↓ [执行一个宏任务] → [再次检查微任务]↓ [重复循环...]
掌握事件循环机制可以帮助你:
-
更好地理解异步代码执行顺序
-
避免常见的并发问题
-
编写性能更高的JavaScript代码