Node.js 事件循环机制

章节介绍

事件循环是 Node.js 的核心机制,它使得 Node.js 能够执行非阻塞 I/O 操作,尽管 JavaScript 是单线程的。理解事件循环对于编写高效的 Node.js 应用程序至关重要。本教程将详细介绍事件循环的工作原理、任务队列的类型以及代码执行顺序。

核心知识点

事件循环概述

Node.js 的事件循环是一个单线程的循环,用于处理异步操作和回调函数。

事件循环工作流程:

┌─────────────────────────────────────┐
│         Node.js 事件循环              │
├─────────────────────────────────────┤
│  1. 执行同步代码                     │
│  2. 执行微任务队列                    │
│  3. 执行宏任务队列                    │
│  4. 重复步骤 2-3                      │
└─────────────────────────────────────┘

任务队列:

┌─────────────────────────────────────┐
│        微任务队列(Microtask)        │
│  - Promise.then/catch/finally        │
│  - process.nextTick                  │
│  - queueMicrotask                    │
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│        宏任务队列(Macrotask)        │
│  - setTimeout/setInterval            │
│  - setImmediate                     │
│  - I/O 回调                          │
│  - close 回调                        │
└─────────────────────────────────────┘

事件循环的各个阶段

Node.js 的事件循环分为多个阶段,每个阶段都有自己的任务队列。

事件循环阶段:

┌─────────────────────────────────────┐
│  1. Timers 阶段                     │
│     - setTimeout/setInterval 回调    │
├─────────────────────────────────────┤
│  2. Pending Callbacks 阶段           │
│     - I/O 异常回调                   │
├─────────────────────────────────────┤
│  3. Idle, Prepare 阶段               │
│     - 内部使用                       │
├─────────────────────────────────────┤
│  4. Poll 阶段                        │
│     - I/O 回调                       │
│     - 新的 I/O 事件                  │
├─────────────────────────────────────┤
│  5. Check 阶段                       │
│     - setImmediate 回调              │
├─────────────────────────────────────┤
│  6. Close Callbacks 阶段             │
│     - close 事件回调                 │
└─────────────────────────────────────┘

微任务与宏任务

理解微任务和宏任务的区别对于掌握事件循环至关重要。

// 微任务(Microtask)
// - Promise.then/catch/finally
// - process.nextTick
// - queueMicrotask

// 宏任务(Macrotask)
// - setTimeout/setInterval
// - setImmediate
// - I/O 回调
// - close 回调

// 示例 1:微任务优先于宏任务
console.log('1. 同步代码');

Promise.resolve().then(() => {
  console.log('2. 微任务(Promise.then)');
});

setTimeout(() => {
  console.log('3. 宏任务(setTimeout)');
}, 0);

console.log('4. 同步代码');

// 输出顺序:
// 1. 同步代码
// 4. 同步代码
// 2. 微任务(Promise.then)
// 3. 宏任务(setTimeout)

// 示例 2:process.nextTick 优先于 Promise.then
console.log('1. 同步代码');

process.nextTick(() => {
  console.log('2. 微任务(process.nextTick)');
});

Promise.resolve().then(() => {
  console.log('3. 微任务(Promise.then)');
});

console.log('4. 同步代码');

// 输出顺序:
// 1. 同步代码
// 4. 同步代码
// 2. 微任务(process.nextTick)
// 3. 微任务(Promise.then)

// 示例 3:微任务队列会全部执行完毕
console.log('1. 同步代码');

Promise.resolve().then(() => {
  console.log('2. 微任务 1');
  Promise.resolve().then(() => {
    console.log('3. 微任务 2(嵌套)');
  });
});

Promise.resolve().then(() => {
  console.log('4. 微任务 3');
});

console.log('5. 同步代码');

// 输出顺序:
// 1. 同步代码
// 5. 同步代码
// 2. 微任务 1
// 4. 微任务 3
// 3. 微任务 2(嵌套)

事件循环示例

// 示例 1:基本的事件循环
console.log('开始');

setTimeout(() => {
  console.log('setTimeout 1');
}, 0);

Promise.resolve().then(() => {
  console.log('Promise 1');
});

console.log('结束');

// 输出顺序:
// 开始
// 结束
// Promise 1
// setTimeout 1

// 示例 2:嵌套的异步操作
console.log('1. 同步代码');

setTimeout(() => {
  console.log('2. setTimeout 1');
  
  Promise.resolve().then(() => {
    console.log('3. Promise 1(在 setTimeout 中)');
  });
}, 0);

Promise.resolve().then(() => {
  console.log('4. Promise 2');
  
  setTimeout(() => {
    console.log('5. setTimeout 2(在 Promise 中)');
  }, 0);
});

console.log('6. 同步代码');

// 输出顺序:
// 1. 同步代码
// 6. 同步代码
// 4. Promise 2
// 2. setTimeout 1
// 3. Promise 1(在 setTimeout 中)
// 5. setTimeout 2(在 Promise 中)

// 示例 3:setImmediate 与 setTimeout
console.log('1. 同步代码');

setImmediate(() => {
  console.log('2. setImmediate');
});

setTimeout(() => {
  console.log('3. setTimeout');
}, 0);

console.log('4. 同步代码');

// 输出顺序(可能不同):
// 1. 同步代码
// 4. 同步代码
// 2. setImmediate
// 3. setTimeout

// 或:
// 1. 同步代码
// 4. 同步代码
// 3. setTimeout
// 2. setImmediate

// 注意:在 I/O 回调中,setImmediate 总是先于 setTimeout

process.nextTick

process.nextTick 是一个特殊的微任务,它在当前操作完成后立即执行。

// 示例 1:process.nextTick 的执行时机
console.log('1. 同步代码');

process.nextTick(() => {
  console.log('2. process.nextTick');
});

console.log('3. 同步代码');

// 输出顺序:
// 1. 同步代码
// 3. 同步代码
// 2. process.nextTick

// 示例 2:process.nextTick 可以递归调用
console.log('1. 同步代码');

let count = 0;
function recursiveNextTick() {
  if (count < 3) {
    count++;
    console.log(`2. process.nextTick ${count}`);
    process.nextTick(recursiveNextTick);
  }
}

recursiveNextTick();

console.log('3. 同步代码');

// 输出顺序:
// 1. 同步代码
// 3. 同步代码
// 2. process.nextTick 1
// 2. process.nextTick 2
// 2. process.nextTick 3

// 示例 3:process.nextTick 与 Promise.then
console.log('1. 同步代码');

process.nextTick(() => {
  console.log('2. process.nextTick 1');
});

Promise.resolve().then(() => {
  console.log('3. Promise.then 1');
});

process.nextTick(() => {
  console.log('4. process.nextTick 2');
});

Promise.resolve().then(() => {
  console.log('5. Promise.then 2');
});

console.log('6. 同步代码');

// 输出顺序:
// 1. 同步代码
// 6. 同步代码
// 2. process.nextTick 1
// 4. process.nextTick 2
// 3. Promise.then 1
// 5. Promise.then 2

setImmediate

setImmediate 在当前事件循环的 Check 阶段执行。

// 示例 1:setImmediate 的基本使用
console.log('1. 同步代码');

setImmediate(() => {
  console.log('2. setImmediate');
});

console.log('3. 同步代码');

// 输出顺序:
// 1. 同步代码
// 3. 同步代码
// 2. setImmediate

// 示例 2:setImmediate 与 setTimeout
console.log('1. 同步代码');

setImmediate(() => {
  console.log('2. setImmediate');
});

setTimeout(() => {
  console.log('3. setTimeout');
}, 0);

console.log('4. 同步代码');

// 输出顺序(可能不同):
// 1. 同步代码
// 4. 同步代码
// 2. setImmediate
// 3. setTimeout

// 或:
// 1. 同步代码
// 4. 同步代码
// 3. setTimeout
// 2. setImmediate

// 示例 3:在 I/O 回调中,setImmediate 总是先于 setTimeout
const fs = require('fs');

fs.readFile(__filename, () => {
  console.log('1. I/O 回调开始');
  
  setImmediate(() => {
    console.log('2. setImmediate(在 I/O 回调中)');
  });
  
  setTimeout(() => {
    console.log('3. setTimeout(在 I/O 回调中)');
  }, 0);
  
  console.log('4. I/O 回调结束');
});

// 输出顺序:
// 1. I/O 回调开始
// 4. I/O 回调结束
// 2. setImmediate(在 I/O 回调中)
// 3. setTimeout(在 I/O 回调中)

实用案例分析

案例 1:事件循环分析器

创建一个能够分析代码执行顺序的事件循环分析器。

// event-loop-analyzer.js
class EventLoopAnalyzer {
  constructor() {
    this.timeline = [];
    this.currentPhase = 'sync';
  }
  
  // 记录事件
  log(message, type = 'sync') {
    const timestamp = Date.now();
    this.timeline.push({
      timestamp,
      message,
      type,
      phase: this.currentPhase
    });
    
    console.log(`[${type.toUpperCase()}] ${message}`);
  }
  
  // 分析执行顺序
  analyze() {
    console.log('\n=== 事件循环分析 ===');
    console.log(`总事件数:${this.timeline.length}`);
    
    const typeCount = {};
    this.timeline.forEach(event => {
      typeCount[event.type] = (typeCount[event.type] || 0) + 1;
    });
    
    console.log('\n事件类型统计:');
    Object.entries(typeCount).forEach(([type, count]) => {
      console.log(`  ${type}: ${count}`);
    });
    
    console.log('\n执行时间线:');
    this.timeline.forEach((event, index) => {
      const timeDiff = index > 0 
        ? event.timestamp - this.timeline[index - 1].timestamp 
        : 0;
      console.log(`  ${index + 1}. [${event.type.toUpperCase()}] ${event.message} (+${timeDiff}ms)`);
    });
  }
  
  // 模拟同步代码
  simulateSync(callback) {
    this.currentPhase = 'sync';
    callback();
  }
  
  // 模拟微任务
  simulateMicrotask(callback) {
    this.currentPhase = 'microtask';
    callback();
  }
  
  // 模拟宏任务
  simulateMacrotask(callback) {
    this.currentPhase = 'macrotask';
    callback();
  }
}

// 使用示例
const analyzer = new EventLoopAnalyzer();

console.log('=== 事件循环模拟 ===');

analyzer.simulateSync(() => {
  analyzer.log('同步代码 1');
});

analyzer.simulateMicrotask(() => {
  analyzer.log('微任务 1(Promise.then)');
});

analyzer.simulateMicrotask(() => {
  analyzer.log('微任务 2(process.nextTick)');
});

analyzer.simulateMacrotask(() => {
  analyzer.log('宏任务 1(setTimeout)');
});

analyzer.simulateSync(() => {
  analyzer.log('同步代码 2');
});

analyzer.simulateMicrotask(() => {
  analyzer.log('微任务 3(Promise.then)');
});

analyzer.simulateMacrotask(() => {
  analyzer.log('宏任务 2(setImmediate)');
});

analyzer.analyze();

案例 2:异步任务调度器

创建一个能够调度和执行异步任务的调度器。

// async-task-scheduler.js
class AsyncTaskScheduler {
  constructor() {
    this.tasks = new Map();
    this.taskId = 0;
  }
  
  // 添加微任务
  addMicrotask(fn, ...args) {
    const id = ++this.taskId;
    const task = {
      id,
      type: 'microtask',
      fn,
      args,
      createdAt: Date.now()
    };
    
    this.tasks.set(id, task);
    
    process.nextTick(() => {
      try {
        task.result = fn(...args);
        task.status = 'completed';
        task.completedAt = Date.now();
      } catch (error) {
        task.error = error;
        task.status = 'failed';
        task.completedAt = Date.now();
      }
    });
    
    return id;
  }
  
  // 添加 Promise 微任务
  addPromiseTask(fn, ...args) {
    const id = ++this.taskId;
    const task = {
      id,
      type: 'promise',
      fn,
      args,
      createdAt: Date.now()
    };
    
    this.tasks.set(id, task);
    
    Promise.resolve().then(async () => {
      try {
        task.result = await fn(...args);
        task.status = 'completed';
        task.completedAt = Date.now();
      } catch (error) {
        task.error = error;
        task.status = 'failed';
        task.completedAt = Date.now();
      }
    });
    
    return id;
  }
  
  // 添加 setTimeout 宏任务
  addTimeoutTask(fn, delay, ...args) {
    const id = ++this.taskId;
    const task = {
      id,
      type: 'timeout',
      fn,
      delay,
      args,
      createdAt: Date.now()
    };
    
    this.tasks.set(id, task);
    
    setTimeout(() => {
      try {
        task.result = fn(...args);
        task.status = 'completed';
        task.completedAt = Date.now();
      } catch (error) {
        task.error = error;
        task.status = 'failed';
        task.completedAt = Date.now();
      }
    }, delay);
    
    return id;
  }
  
  // 添加 setImmediate 宏任务
  addImmediateTask(fn, ...args) {
    const id = ++this.taskId;
    const task = {
      id,
      type: 'immediate',
      fn,
      args,
      createdAt: Date.now()
    };
    
    this.tasks.set(id, task);
    
    setImmediate(() => {
      try {
        task.result = fn(...args);
        task.status = 'completed';
        task.completedAt = Date.now();
      } catch (error) {
        task.error = error;
        task.status = 'failed';
        task.completedAt = Date.now();
      }
    });
    
    return id;
  }
  
  // 获取任务状态
  getTaskStatus(id) {
    return this.tasks.get(id);
  }
  
  // 等待任务完成
  async waitForTask(id) {
    return new Promise((resolve, reject) => {
      const checkTask = () => {
        const task = this.tasks.get(id);
        
        if (!task) {
          reject(new Error('Task not found'));
          return;
        }
        
        if (task.status === 'completed') {
          resolve(task.result);
        } else if (task.status === 'failed') {
          reject(task.error);
        } else {
          process.nextTick(checkTask);
        }
      };
      
      checkTask();
    });
  }
  
  // 获取所有任务
  getAllTasks() {
    return Array.from(this.tasks.values());
  }
  
  // 清除已完成的任务
  clearCompletedTasks() {
    this.tasks.forEach((task, id) => {
      if (task.status === 'completed' || task.status === 'failed') {
        this.tasks.delete(id);
      }
    });
  }
}

// 使用示例
const scheduler = new AsyncTaskScheduler();

// 添加各种任务
const task1 = scheduler.addMicrotask(() => {
  console.log('微任务 1 执行');
  return '微任务 1 结果';
});

const task2 = scheduler.addPromiseTask(async () => {
  console.log('Promise 任务执行');
  await new Promise(resolve => setTimeout(resolve, 100));
  return 'Promise 任务结果';
});

const task3 = scheduler.addTimeoutTask(() => {
  console.log('setTimeout 任务执行');
  return 'setTimeout 任务结果';
}, 50);

const task4 = scheduler.addImmediateTask(() => {
  console.log('setImmediate 任务执行');
  return 'setImmediate 任务结果';
});

// 等待任务完成
(async () => {
  try {
    const result1 = await scheduler.waitForTask(task1);
    console.log('任务 1 结果:', result1);
    
    const result2 = await scheduler.waitForTask(task2);
    console.log('任务 2 结果:', result2);
    
    const result3 = await scheduler.waitForTask(task3);
    console.log('任务 3 结果:', result3);
    
    const result4 = await scheduler.waitForTask(task4);
    console.log('任务 4 结果:', result4);
    
    console.log('\n所有任务状态:');
    const allTasks = scheduler.getAllTasks();
    allTasks.forEach(task => {
      console.log(`任务 ${task.id}: ${task.type} - ${task.status}`);
      console.log(`  执行时间:${task.completedAt - task.createdAt}ms`);
    });
  } catch (error) {
    console.error('任务执行失败:', error);
  }
})();

案例 3:事件循环可视化

创建一个能够可视化事件循环过程的工具。

// event-loop-visualizer.js
class EventLoopVisualizer {
  constructor() {
    this.phases = {
      sync: [],
      microtask: [],
      macrotask: []
    };
    this.executionOrder = [];
  }
  
  // 添加同步任务
  addSyncTask(name) {
    this.phases.sync.push({
      name,
      type: 'sync',
      timestamp: Date.now()
    });
  }
  
  // 添加微任务
  addMicrotask(name) {
    this.phases.microtask.push({
      name,
      type: 'microtask',
      timestamp: Date.now()
    });
  }
  
  // 添加宏任务
  addMacrotask(name) {
    this.phases.macrotask.push({
      name,
      type: 'macrotask',
      timestamp: Date.now()
    });
  }
  
  // 记录执行
  recordExecution(phase, task) {
    this.executionOrder.push({
      phase,
      task,
      timestamp: Date.now()
    });
  }
  
  // 可视化执行过程
  visualize() {
    console.log('\n=== 事件循环可视化 ===\n');
    
    // 显示任务队列
    console.log('任务队列:');
    console.log('┌─────────────────────────────────────┐');
    console.log('│ 同步任务队列:                        │');
    this.phases.sync.forEach((task, index) => {
      console.log(`│   ${index + 1}. ${task.name}                 │`);
    });
    console.log('├─────────────────────────────────────┤');
    console.log('│ 微任务队列:                          │');
    this.phases.microtask.forEach((task, index) => {
      console.log(`│   ${index + 1}. ${task.name}                 │`);
    });
    console.log('├─────────────────────────────────────┤');
    console.log('│ 宏任务队列:                          │');
    this.phases.macrotask.forEach((task, index) => {
      console.log(`│   ${index + 1}. ${task.name}                 │`);
    });
    console.log('└─────────────────────────────────────┘\n');
    
    // 显示执行顺序
    console.log('执行顺序:');
    this.executionOrder.forEach((execution, index) => {
      const timeDiff = index > 0 
        ? execution.timestamp - this.executionOrder[index - 1].timestamp 
        : 0;
      console.log(`${index + 1}. [${execution.phase.toUpperCase()}] ${execution.task.name} (+${timeDiff}ms)`);
    });
  }
  
  // 模拟事件循环
  simulate() {
    console.log('=== 模拟事件循环 ===\n');
    
    // 执行同步任务
    console.log('执行同步任务:');
    while (this.phases.sync.length > 0) {
      const task = this.phases.sync.shift();
      this.recordExecution('sync', task);
      console.log(`  执行:${task.name}`);
    }
    
    // 执行微任务
    console.log('\n执行微任务:');
    while (this.phases.microtask.length > 0) {
      const task = this.phases.microtask.shift();
      this.recordExecution('microtask', task);
      console.log(`  执行:${task.name}`);
    }
    
    // 执行宏任务
    console.log('\n执行宏任务:');
    while (this.phases.macrotask.length > 0) {
      const task = this.phases.macrotask.shift();
      this.recordExecution('macrotask', task);
      console.log(`  执行:${task.name}`);
    }
    
    console.log('\n事件循环完成');
  }
}

// 使用示例
const visualizer = new EventLoopVisualizer();

// 添加任务
visualizer.addSyncTask('同步代码 1');
visualizer.addSyncTask('同步代码 2');

visualizer.addMicrotask('Promise.then 1');
visualizer.addMicrotask('process.nextTick 1');
visualizer.addMicrotask('Promise.then 2');

visualizer.addMacrotask('setTimeout 1');
visualizer.addMacrotask('setImmediate 1');
visualizer.addMacrotask('setTimeout 2');

// 模拟事件循环
visualizer.simulate();

// 可视化结果
visualizer.visualize();

代码示例

示例 1:事件循环测试

// event-loop-test.js
console.log('=== 事件循环测试 ===\n');

// 测试 1:基本顺序
console.log('测试 1:基本顺序');
console.log('1. 同步代码');

setTimeout(() => {
  console.log('4. setTimeout');
}, 0);

Promise.resolve().then(() => {
  console.log('3. Promise.then');
});

console.log('2. 同步代码');

// 测试 2:嵌套异步操作
console.log('\n测试 2:嵌套异步操作');
console.log('1. 同步代码');

setTimeout(() => {
  console.log('3. setTimeout 1');
  
  Promise.resolve().then(() => {
    console.log('5. Promise.then(嵌套)');
  });
  
  setTimeout(() => {
    console.log('6. setTimeout(嵌套)');
  }, 0);
}, 0);

Promise.resolve().then(() => {
  console.log('2. Promise.then');
  
  setTimeout(() => {
    console.log('4. setTimeout(在 Promise 中)');
  }, 0);
});

// 测试 3:process.nextTick 优先级
console.log('\n测试 3:process.nextTick 优先级');
console.log('1. 同步代码');

process.nextTick(() => {
  console.log('3. process.nextTick 1');
});

Promise.resolve().then(() => {
  console.log('4. Promise.then');
});

process.nextTick(() => {
  console.log('2. process.nextTick 2');
});

console.log('5. 同步代码');

// 测试 4:setImmediate 与 setTimeout
console.log('\n测试 4:setImmediate 与 setTimeout');
console.log('1. 同步代码');

setImmediate(() => {
  console.log('3. setImmediate');
});

setTimeout(() => {
  console.log('4. setTimeout');
}, 0);

console.log('2. 同步代码');

// 测试 5:I/O 回调中的顺序
console.log('\n测试 5:I/O 回调中的顺序');
const fs = require('fs');

fs.readFile(__filename, () => {
  console.log('1. I/O 回调开始');
  
  setImmediate(() => {
    console.log('3. setImmediate(在 I/O 回调中)');
  });
  
  setTimeout(() => {
    console.log('4. setTimeout(在 I/O 回调中)');
  }, 0);
  
  Promise.resolve().then(() => {
    console.log('2. Promise.then(在 I/O 回调中)');
  });
  
  console.log('5. I/O 回调结束');
});

示例 2:异步操作顺序分析

// async-order-analyzer.js
class AsyncOrderAnalyzer {
  constructor() {
    this.events = [];
  }
  
  // 记录事件
  record(type, description) {
    const timestamp = Date.now();
    this.events.push({ type, description, timestamp });
    console.log(`[${type.toUpperCase()}] ${description}`);
  }
  
  // 分析执行顺序
  analyze() {
    console.log('\n=== 执行顺序分析 ===');
    console.log(`总事件数:${this.events.length}\n`);
    
    const typeGroups = {};
    this.events.forEach(event => {
      if (!typeGroups[event.type]) {
        typeGroups[event.type] = [];
      }
      typeGroups[event.type].push(event);
    });
    
    Object.entries(typeGroups).forEach(([type, events]) => {
      console.log(`${type.toUpperCase()} 事件(${events.length} 个):`);
      events.forEach((event, index) => {
        const timeDiff = index > 0 
          ? event.timestamp - events[index - 1].timestamp 
          : 0;
        console.log(`  ${index + 1}. ${event.description} (+${timeDiff}ms)`);
      });
      console.log();
    });
  }
}

// 使用示例
const analyzer = new AsyncOrderAnalyzer();

console.log('=== 异步操作顺序测试 ===\n');

analyzer.record('sync', '同步代码 1');

setTimeout(() => {
  analyzer.record('timeout', 'setTimeout 1');
}, 0);

Promise.resolve().then(() => {
  analyzer.record('promise', 'Promise.then 1');
  
  setTimeout(() => {
    analyzer.record('timeout', 'setTimeout 2(嵌套)');
  }, 0);
});

process.nextTick(() => {
  analyzer.record('nexttick', 'process.nextTick');
});

setImmediate(() => {
  analyzer.record('immediate', 'setImmediate');
});

analyzer.record('sync', '同步代码 2');

setTimeout(() => {
  analyzer.record('timeout', 'setTimeout 3');
}, 100);

Promise.resolve().then(() => {
  analyzer.record('promise', 'Promise.then 2');
});

setTimeout(() => {
  analyzer.analyze();
}, 200);

示例 3:事件循环性能测试

// event-loop-performance.js
class EventLoopPerformanceTester {
  constructor() {
    this.results = [];
  }
  
  // 测试同步操作性能
  testSync(iterations) {
    console.log(`\n测试同步操作(${iterations} 次迭代)`);
    const start = Date.now();
    
    for (let i = 0; i < iterations; i++) {
      const result = i * 2;
    }
    
    const duration = Date.now() - start;
    console.log(`执行时间:${duration}ms`);
    console.log(`平均每次:${(duration / iterations).toFixed(4)}ms`);
    
    this.results.push({
      type: 'sync',
      iterations,
      duration,
      avgTime: duration / iterations
    });
  }
  
  // 测试 process.nextTick 性能
  testNextTick(iterations) {
    console.log(`\n测试 process.nextTick(${iterations} 次迭代)`);
    const start = Date.now();
    let completed = 0;
    
    function recursiveNextTick() {
      if (completed < iterations) {
        completed++;
        process.nextTick(recursiveNextTick);
      } else {
        const duration = Date.now() - start;
        console.log(`执行时间:${duration}ms`);
        console.log(`平均每次:${(duration / iterations).toFixed(4)}ms`);
        
        this.results.push({
          type: 'nexttick',
          iterations,
          duration,
          avgTime: duration / iterations
        });
      }
    }
    
    recursiveNextTick();
  }
  
  // 测试 Promise 性能
  testPromise(iterations) {
    console.log(`\n测试 Promise(${iterations} 次迭代)`);
    const start = Date.now();
    let completed = 0;
    
    function recursivePromise() {
      if (completed < iterations) {
        completed++;
        Promise.resolve().then(recursivePromise);
      } else {
        const duration = Date.now() - start;
        console.log(`执行时间:${duration}ms`);
        console.log(`平均每次:${(duration / iterations).toFixed(4)}ms`);
        
        this.results.push({
          type: 'promise',
          iterations,
          duration,
          avgTime: duration / iterations
        });
      }
    }
    
    recursivePromise();
  }
  
  // 测试 setTimeout 性能
  testTimeout(iterations, delay) {
    console.log(`\n测试 setTimeout(${iterations} 次迭代,延迟 ${delay}ms)`);
    const start = Date.now();
    let completed = 0;
    
    function recursiveTimeout() {
      if (completed < iterations) {
        completed++;
        setTimeout(recursiveTimeout, delay);
      } else {
        const duration = Date.now() - start;
        console.log(`执行时间:${duration}ms`);
        console.log(`平均每次:${(duration / iterations).toFixed(4)}ms`);
        
        this.results.push({
          type: 'timeout',
          iterations,
          delay,
          duration,
          avgTime: duration / iterations
        });
      }
    }
    
    recursiveTimeout();
  }
  
  // 测试 setImmediate 性能
  testImmediate(iterations) {
    console.log(`\n测试 setImmediate(${iterations} 次迭代)`);
    const start = Date.now();
    let completed = 0;
    
    function recursiveImmediate() {
      if (completed < iterations) {
        completed++;
        setImmediate(recursiveImmediate);
      } else {
        const duration = Date.now() - start;
        console.log(`执行时间:${duration}ms`);
        console.log(`平均每次:${(duration / iterations).toFixed(4)}ms`);
        
        this.results.push({
          type: 'immediate',
          iterations,
          duration,
          avgTime: duration / iterations
        });
      }
    }
    
    recursiveImmediate();
  }
  
  // 生成性能报告
  generateReport() {
    console.log('\n=== 性能测试报告 ===\n');
    
    const report = this.results.map(result => {
      return {
        类型: result.type,
        迭代次数: result.iterations,
        总时间: `${result.duration}ms`,
        平均时间: `${result.avgTime.toFixed(4)}ms`
      };
    });
    
    console.table(report);
  }
}

// 使用示例
const tester = new EventLoopPerformanceTester();

// 测试同步操作
tester.testSync(1000000);

// 测试 process.nextTick
tester.testNextTick(10000);

// 测试 Promise
tester.testPromise(10000);

// 测试 setTimeout
tester.testTimeout(1000, 0);

// 测试 setImmediate
tester.testImmediate(10000);

// 生成性能报告
setTimeout(() => {
  tester.generateReport();
}, 5000);

实现技巧与注意事项

事件循环使用建议

  1. 理解执行顺序:掌握微任务和宏任务的执行顺序
  2. 避免阻塞事件循环:不要在同步代码中执行耗时操作
  3. 合理使用异步操作:根据需求选择合适的异步方法
  4. 注意递归调用:避免无限递归导致事件循环阻塞

性能优化建议

  1. 使用 process.nextTick:对于需要立即执行的任务
  2. 使用 Promise:对于链式异步操作
  3. 使用 setImmediate:在 I/O 回调中优先使用
  4. 避免过多的微任务:防止微任务队列过长

调试技巧

  1. 使用 console.log:在关键位置打印日志
  2. 分析执行顺序:使用事件循环分析器
  3. 监控性能:使用性能测试工具
  4. 避免回调地狱:使用 Promise 或 async/await

常见问题与解决方案

问题 1:事件循环阻塞

// 问题代码:同步代码阻塞事件循环
function heavyComputation() {
  let sum = 0;
  for (let i = 0; i < 1000000000; i++) {
    sum += i;
  }
  return sum;
}

console.log('开始计算');
const result = heavyComputation();
console.log('计算完成', result);
// 在计算期间,事件循环被阻塞

// 解决方案:使用 setImmediate 分割任务
function heavyComputationAsync(callback) {
  let sum = 0;
  let i = 0;
  const chunkSize = 10000000;
  
  function processChunk() {
    const end = Math.min(i + chunkSize, 1000000000);
    
    for (; i < end; i++) {
      sum += i;
    }
    
    if (i < 1000000000) {
      setImmediate(processChunk);
    } else {
      callback(sum);
    }
  }
  
  processChunk();
}

console.log('开始计算');
heavyComputationAsync((result) => {
  console.log('计算完成', result);
});
console.log('事件循环未被阻塞');

问题 2:微任务队列过长

// 问题代码:微任务队列过长
function createMicrotasks() {
  for (let i = 0; i < 100000; i++) {
    Promise.resolve().then(() => {
      console.log('微任务', i);
    });
  }
}

createMicrotasks();
// 微任务队列过长,影响性能

// 解决方案:使用宏任务分割微任务
function createMicrotasksOptimized() {
  let i = 0;
  const chunkSize = 1000;
  
  function processChunk() {
    const end = Math.min(i + chunkSize, 100000);
    
    for (; i < end; i++) {
      Promise.resolve().then(() => {
        console.log('微任务', i);
      });
    }
    
    if (i < 100000) {
      setImmediate(processChunk);
    }
  }
  
  processChunk();
}

createMicrotasksOptimized();

问题 3:递归调用导致栈溢出

// 问题代码:递归调用导致栈溢出
function recursiveFunction(n) {
  if (n > 0) {
    recursiveFunction(n - 1);
  }
}

recursiveFunction(100000);
// RangeError: Maximum call stack size exceeded

// 解决方案:使用 process.nextTick 避免栈溢出
function recursiveFunctionAsync(n) {
  if (n > 0) {
    process.nextTick(() => {
      recursiveFunctionAsync(n - 1);
    });
  }
}

recursiveFunctionAsync(100000);
// 不会导致栈溢出

问题 4:异步操作顺序不确定

// 问题代码:异步操作顺序不确定
console.log('1');

setTimeout(() => {
  console.log('2');
}, 0);

Promise.resolve().then(() => {
  console.log('3');
});

console.log('4');
// 输出顺序:1, 4, 3, 2

// 解决方案:使用 Promise 或 async/await 控制顺序
async function controlledOrder() {
  console.log('1');
  
  await new Promise(resolve => {
    setTimeout(() => {
      console.log('2');
      resolve();
    }, 0);
  });
  
  await Promise.resolve().then(() => {
    console.log('3');
  });
  
  console.log('4');
}

controlledOrder();
// 输出顺序:1, 2, 3, 4

总结

本教程详细介绍了 Node.js 的事件循环机制,包括事件循环的工作原理、任务队列的类型、微任务与宏任务的区别等重要内容。掌握事件循环对于理解 Node.js 的异步编程模型至关重要。

通过本集的学习,您应该能够:

  1. 理解 Node.js 事件循环的工作原理
  2. 掌握微任务和宏任务的区别
  3. 理解 process.nextTick 和 setImmediate 的使用场景
  4. 分析代码的执行顺序
  5. 避免事件循环阻塞
  6. 优化异步操作性能

在下一集中,我们将学习 Node.js 的回调函数,这是 Node.js 异步编程的基础。继续加油,您的 Node.js 技能正在不断提升!

« 上一篇 Node.js 路径与 URL 模块 下一篇 » Node.js 回调函数