浏览器与Node.js事件循环(Event Loop)详解
浏览器事件循环(Event Loop)
1. 基本概念
浏览器的事件循环是JavaScript实现异步编程的核心机制,它负责协调用户交互、脚本执行、渲染、网络请求等事件。
2. 运行机制
主要组成部分:
- **调用栈(Call Stack)**:执行同步代码的栈结构
- **任务队列(Task Queue)**:
- 宏任务队列(Macrotask Queue)
- 微任务队列(Microtask Queue)
- Web APIs:浏览器提供的异步API环境
执行流程:
- 执行全局同步代码(同步任务直接进入调用栈执行)
- 遇到异步操作时:
- 宏任务(setTimeout、setInterval、I/O等)交给Web APIs处理,完成后回调放入宏任务队列
- 微任务(Promise.then、MutationObserver等)回调放入微任务队列
- 当调用栈为空时:
- 先检查微任务队列并执行所有微任务
- 然后从宏任务队列取出一个任务执行
- 重复上述过程
图示流程:
[执行同步代码] → [微任务队列全部执行] → [渲染(如有需要)] → [取一个宏任务执行] → [重复]
3. 任务分类
宏任务(Macrotasks):
- script整体代码
- setTimeout/setInterval
- I/O操作
- UI渲染
- postMessage
- MessageChannel
- setImmediate(仅IE/Node.js)
微任务(Microtasks):
- Promise.then/catch/finally
- MutationObserver
- process.nextTick(Node.js特有)
- queueMicrotask
4. 执行顺序示例
1 | console.log('script start'); |
Node.js事件循环
1. 基本概念
Node.js使用libuv库实现的事件驱动架构,其事件循环比浏览器更复杂,分为多个阶段。
2. 阶段划分
完整事件循环阶段(按顺序执行):
- timers阶段:执行setTimeout和setInterval的回调
- pending callbacks阶段:执行某些系统操作的回调(如TCP错误)
- idle, prepare阶段:Node内部使用
- poll阶段:
- 检索新的I/O事件
- 执行I/O相关回调(几乎所有的回调,除了close、timers和setImmediate)
- 可能会阻塞在此阶段等待新事件
- check阶段:执行setImmediate的回调
- close callbacks阶段:执行关闭事件的回调(如socket.on(‘close’))
3. 特殊队列
nextTick队列
process.nextTick()的回调- 不属于事件循环的任何阶段
- 在当前操作完成后立即执行,优先级高于微任务
微任务队列
- 包括Promise.then、queueMicrotask等
- 在每个阶段之间执行
4. 执行顺序规则
- nextTick队列 > 微任务队列 > 其他阶段
- 每个阶段执行完毕后,都会先执行完nextTick队列和微任务队列
5. 与浏览器的区别
- 阶段划分不同:Node.js有明确的阶段划分
- 优先级不同:
- Node.js中:process.nextTick > Promise微任务
- 浏览器中:Promise微任务 > setTimeout宏任务
- 实现机制不同:Node.js基于libuv,浏览器基于HTML5规范
6. 执行顺序示例
1 | console.log('start'); |
常见面试问题解答
Q1: 浏览器和Node.js事件循环的主要区别?
- 阶段划分:Node.js有明确的6个阶段,浏览器更简单
- 任务优先级:Node.js中process.nextTick优先级最高
- 实现机制:Node.js使用libuv,浏览器遵循HTML5规范
- 微任务执行时机:Node.js在每个阶段之间执行微任务
Q2: process.nextTick和setImmediate的区别?
process.nextTick():- 在当前阶段立即执行
- 优先级高于微任务
setImmediate():- 在check阶段执行
- 属于宏任务
Q3: 为什么Promise.then比setTimeout先执行?
因为Promise.then属于微任务,会在当前宏任务执行完后立即执行所有微任务,而setTimeout是下一个宏任务。
Q4: Node.js中哪些操作会产生微任务?
- Promise.then/catch/finally
- async/await
- queueMicrotask
- process.nextTick(虽然严格来说不属于微任务,但行为类似且优先级更高)