浏览器和 Node.js 的事件循环有什么区别?

浏览器事件循环

同步代码 → 微任务全部执行 → 一个宏任务 → 微任务全部执行 → 一个宏任务 → ...

微任务:Promise.then、queueMicrotask、MutationObserver
宏任务:setTimeout、setInterval、I/O、UI 渲染

Node.js 事件循环

┌───────────────────────────┐
┌─>│         timers             │  ← setTimeout/setInterval
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks      │  ← 系统级回调
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare        │  ← 内部使用
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │           poll             │  ← I/O 事件
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │           check             │  ← setImmediate
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │      close callbacks       │  ← close 事件
│  └───────────────────────────┘
│
└── 每个阶段之间执行微任务

关键区别

特性 浏览器 Node.js
微任务执行时机 每个宏任务后 每个阶段切换时
process.nextTick 无 有,优先级最高
setImmediate 无 有,check 阶段执行
微任务类型 Promise.then 等 + process.nextTick

经典面试题:setTimeout vs setImmediate

// 顺序不确定(取决于事件循环启动时间)
setTimeout(() => console.log('timeout'), 0)
setImmediate(() => console.log('immediate'))

// 但在 I/O 回调中,setImmediate 一定先于 setTimeout
fs.readFile('file.txt', () => {
  setTimeout(() => console.log('timeout'), 0)
  setImmediate(() => console.log('immediate'))
  // 总是输出: immediate → timeout
})

process.nextTick

Promise.resolve().then(() => console.log('promise'))
process.nextTick(() => console.log('nextTick'))
// 输出: nextTick → promise
// nextTick 优先级高于所有微任务

Node.js 11+ 的变化

Node.js 11 开始,浏览器和 Node.js 的微任务执行时机趋于一致:每个宏任务后立即执行微任务,而非等到当前阶段所有宏任务都执行完。

同分类其他题目