Node.js setImmediate

一、setImmediate 基本概念

1.1 什么是 setImmediate

setImmediate 是 Node.js 提供的一个定时器函数,用于将回调函数推迟到当前事件循环的 check 阶段 执行。

1.2 与 setTimeout 的区别

// 执行顺序比较
console.log('开始');

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

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

console.log('结束');

// 输出顺序可能是:
// 开始
// 结束
// setTimeout 0
// setImmediate
// 或者:
// 开始
// 结束
// setImmediate
// setTimeout 0

二、setImmediate 的工作原理

2.1 在事件循环中的位置

const fs = require('fs');

// 在I/O回调中,setImmediate总是先于setTimeout执行
fs.readFile(__filename, () => {
  console.log('I/O回调开始');
  
  setTimeout(() => {
    console.log('setTimeout在I/O回调中');
  }, 0);
  
  setImmediate(() => {
    console.log('setImmediate在I/O回调中');
  });
  
  console.log('I/O回调结束');
});

// 输出顺序:
// I/O回调开始
// I/O回调结束
// setImmediate在I/O回调中
// setTimeout在I/O回调中

2.2 与 process.nextTick 的比较

console.log('开始');

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

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

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

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

console.log('结束');

// 输出顺序:
// 开始
// 结束
// nextTick 1
// nextTick 2
// setImmediate 1
// setImmediate 2

三、setImmediate 的实际应用

3.1 分解CPU密集型任

function processLargeArray(array, chunkSize = 1000) {
  return new Promise((resolve) => {
    let results = [];
    let index = 0;
    
    function processChunk() {
      const chunk = array.slice(index, index + chunkSize);
      
      // 处理当前块
      const chunkResults = chunk.map(item => item * 2);
      results = results.concat(chunkResults);
      
      index += chunkSize;
      
      if (index < array.length) {
        // 使用setImmediate让出事件循环
        setImmediate(processChunk);
      } else {
        resolve(results);
      }
    }
    
    processChunk();
  });
}

// 使用示例
async function example() {
  const largeArray = Array.from({ length: 10000 }, (_, i) => i);
  const result = await processLargeArray(largeArray);
  console.log(`处理完成,共${result.length}个元素`);
}

example();

3.2 避免I/O回调中的阻塞

const http = require('http');
const fs = require('fs');

const server = http.createServer((req, res) => {
  if (req.url === '/heavy-computation') {
    // ❌ 错误做法:直接在请求处理中执行耗时操作
    // const result = heavyComputation();
    // res.end(result.toString());
    
    // ✅ 正确做法:使用setImmediate分解任务
    setImmediate(() => {
      let result = 0;
      let steps = 0;
      const totalSteps = 1000000;
      
      function computeChunk() {
        for (let i = 0; i < 10000; i++) {
          if (steps >= totalSteps) {
            res.end(`计算结果: ${result}`);
            return;
          }
          result += Math.sin(steps) * Math.cos(steps);
          steps++;
        }
        
        setImmediate(computeChunk);
      }
      
      computeChunk();
    });
  } else {
    res.end('Hello World');
  }
});

server.listen(3000);

3.3 批量操作优化

class BatchProcessor {
  constructor() {
    this.queue = [];
    this.processing = false;
    this.batchSize = 100;
  }
  
  add(item) {
    this.queue.push(item);
    
    if (!this.processing) {
      this.processing = true;
      this.processBatch();
    }
  }
  
  processBatch() {
    if (this.queue.length === 0) {
      this.processing = false;
      return;
    }
    
    const batch = this.queue.splice(0, this.batchSize);
    
    // 处理当前批次
    this.processItems(batch).then(() => {
      // 使用setImmediate处理下一批次,让出事件循环
      setImmediate(() => this.processBatch());
    });
  }
  
  async processItems(items) {
    // 模拟异步处理
    await new Promise(resolve => setTimeout(resolve, 10));
    console.log(`处理了 ${items.length} 个项目`);
  }
}

// 使用示例
const processor = new BatchProcessor();

// 添加大量项目
for (let i = 0; i < 1000; i++) {
  processor.add(`项目-${i}`);
}

四、高级用法与模式

4.1 Promise 封装

// 将setImmediate封装为Promise
function immediatePromise() {
  return new Promise((resolve) => {
    setImmediate(() => {
      resolve();
    });
  });
}

// 异步迭代器中使用
async function* asyncGenerator() {
  for (let i = 0; i < 5; i++) {
    // 在每次迭代间让出事件循环
    await immediatePromise();
    yield i;
  }
}

// 使用示例
(async () => {
  for await (const value of asyncGenerator()) {
    console.log(value);
  }
})();

4.2 递归任务调度

class TaskScheduler {
  constructor(concurrency = 1) {
    this.concurrency = concurrency;
    this.running = 0;
    this.queue = [];
  }
  
  add(task) {
    return new Promise((resolve, reject) => {
      this.queue.push({ task, resolve, reject });
      this.schedule();
    });
  }
  
  schedule() {
    while (this.running < this.concurrency && this.queue.length > 0) {
      const { task, resolve, reject } = this.queue.shift();
      this.running++;
      
      setImmediate(async () => {
        try {
          const result = await task();
          resolve(result);
        } catch (error) {
          reject(error);
        } finally {
          this.running--;
          this.schedule();
        }
      });
    }
  }
}

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

// 添加多个任务
for (let i = 0; i < 10; i++) {
  scheduler.add(async () => {
    console.log(`执行任务 ${i}`);
    await new Promise(resolve => setTimeout(resolve, 1000));
    return `任务 ${i} 完成`;
  }).then(console.log);
}

4.3 性能监控与调试

// 监控setImmediate的执行时间
function createMonitoredImmediate(callback, name = 'anonymous') {
  const start = process.hrtime();
  
  setImmediate(() => {
    const diff = process.hrtime(start);
    const duration = diff[0] * 1000 + diff[1] / 1000000; // 转换为毫秒
    
    console.log(`[setImmediate监控] ${name}: ${duration.toFixed(3)}ms`);
    
    // 如果执行时间过长,发出警告
    if (duration > 10) {
      console.warn(`⚠️ setImmediate回调执行时间过长: ${duration.toFixed(3)}ms`);
    }
    
    callback();
  });
}

// 使用监控版本
createMonitoredImmediate(() => {
  // 模拟一些工作
  let sum = 0;
  for (let i = 0; i < 1000000; i++) {
    sum += i;
  }
}, '计算任务');

五、最佳实践与注意事项

5.1 避免常见陷阱

// ❌ 错误:在递归中使用setImmediate可能导致内存泄漏
function dangerousRecursion(count = 0) {
  if (count >= 1000000) return;
  
  setImmediate(() => {
    dangerousRecursion(count + 1); // 这会创建大量闭包
  });
}

// ✅ 正确:使用迭代方式
function safeIteration() {
  let count = 0;
  
  function next() {
    if (count >= 1000000) return;
    
    count++;
    setImmediate(next); // 重用同一个函数
  }
  
  next();
}

5.2 与 async/await 结合

// 使用setImmediate实现异步队列
class AsyncQueue {
  constructor() {
    this.queue = [];
    this.processing = false;
  }
  
  enqueue(task) {
    return new Promise((resolve, reject) => {
      this.queue.push({ task, resolve, reject });
      
      if (!this.processing) {
        this.processQueue();
      }
    });
  }
  
  async processQueue() {
    this.processing = true;
    
    while (this.queue.length > 0) {
      const { task, resolve, reject } = this.queue.shift();
      
      try {
        const result = await task();
        resolve(result);
      } catch (error) {
        reject(error);
      }
      
      // 在每个任务之间插入setImmediate
      if (this.queue.length > 0) {
        await new Promise(resolve => setImmediate(resolve));
      }
    }
    
    this.processing = false;
  }
}

// 使用示例
const queue = new AsyncQueue();

// 添加多个任务
for (let i = 0; i < 5; i++) {
  queue.enqueue(async () => {
    console.log(`执行任务 ${i}`);
    await new Promise(resolve => setTimeout(resolve, 100));
    return `任务 ${i} 结果`;
  }).then(console.log);
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星空下的DeppBing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值