一、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);
}
857

被折叠的 条评论
为什么被折叠?



