今天发布了Node.js版本0.10,并引入了setImmediate。API changes文档建议在进行递归nextTick调用时使用它。
从MDN says上看,它与process.nextTick非常相似。
什么时候应该使用nextTick?什么时候应该使用setImmediate?
发布于 2013-03-12 06:26:04
如果要在事件队列中已存在的任何I/O事件回调之后对函数进行排队,请使用setImmediate。使用process.nextTick将函数有效地排在事件队列的前面,以便它在当前函数完成后立即执行。
因此,在尝试使用递归分解一个长时间运行的、CPU受限的作业的情况下,您现在可能希望使用setImmediate而不是process.nextTick来对下一次迭代进行排队,否则任何I/O事件回调都没有机会在两次迭代之间运行。
发布于 2017-02-04 02:38:59
举个例子:
import fs from 'fs';
import http from 'http';
const options = {
host: 'www.stackoverflow.com',
port: 80,
path: '/index.html'
};
describe('deferredExecution', () => {
it('deferredExecution', (done) => {
console.log('Start');
setTimeout(() => console.log('setTimeout 1'), 0);
setImmediate(() => console.log('setImmediate 1'));
process.nextTick(() => console.log('nextTick 1'));
setImmediate(() => console.log('setImmediate 2'));
process.nextTick(() => console.log('nextTick 2'));
http.get(options, () => console.log('network IO'));
fs.readdir(process.cwd(), () => console.log('file system IO 1'));
setImmediate(() => console.log('setImmediate 3'));
process.nextTick(() => console.log('nextTick 3'));
setImmediate(() => console.log('setImmediate 4'));
fs.readdir(process.cwd(), () => console.log('file system IO 2'));
console.log('End');
setTimeout(done, 1500);
});
});将给出以下输出
Start // synchronous
End // synchronous
nextTick 1 // microtask
nextTick 2 // microtask
nextTick 3 // microtask
setTimeout 1 // macrotask
file system IO 1 // macrotask
file system IO 2 // macrotask
setImmediate 1 // macrotask
setImmediate 2 // macrotask
setImmediate 3 // macrotask
setImmediate 4 // macrotask
network IO // macrotask我希望这能帮助理解其中的区别。
更新:
使用process.nextTick()延迟的
回调在触发任何其他I/O事件之前运行,而使用setImmediate()时,执行在队列中已经存在的任何I/O事件之后排队。
Node.js Design Pattern,Mario Casciaro著(可能是关于node.js/js的最好的书)
发布于 2017-05-31 11:19:35
我想我可以很好地说明这一点。由于nextTick是在当前操作结束时调用的,因此以递归方式调用它可能会阻止事件循环继续。setImmediate通过在事件循环的检查阶段触发来解决此问题,从而允许事件循环正常继续。
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘来源:https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
请注意,检查阶段紧跟在轮询阶段之后。这是因为轮询阶段和I/O回调是对setImmediate的调用最有可能运行的地方。因此,理想情况下,这些调用中的大多数实际上都是非常即时的,只是不像nextTick那样即时,后者在每次操作后都会进行检查,从技术上讲,它存在于事件循环之外。
让我们看一下setImmediate和process.nextTick之间的区别的一个小例子
function step(iteration) {
if (iteration === 10) return;
setImmediate(() => {
console.log(`setImmediate iteration: ${iteration}`);
step(iteration + 1); // Recursive call from setImmediate handler.
});
process.nextTick(() => {
console.log(`nextTick iteration: ${iteration}`);
});
}
step(0);假设我们刚刚运行了这个程序,并且正在单步执行事件循环的第一次迭代。它将调用迭代为零的step函数。然后,它将注册两个处理程序,一个用于setImmediate,另一个用于process.nextTick。然后,我们从将在下一个检查阶段运行的setImmediate处理程序中递归调用此函数。nextTick处理程序将在当前操作结束时运行,从而中断事件循环,因此即使它是第二个注册的,它实际上也将首先运行。
最终的顺序是:nextTick在当前操作结束时触发,下一个事件循环开始,执行正常的事件循环阶段,setImmediate触发并递归调用我们的step函数以重新启动该过程。当前操作结束、nextTick触发等。
上述代码的输出将是:
nextTick iteration: 0
setImmediate iteration: 0
nextTick iteration: 1
setImmediate iteration: 1
nextTick iteration: 2
setImmediate iteration: 2
nextTick iteration: 3
setImmediate iteration: 3
nextTick iteration: 4
setImmediate iteration: 4
nextTick iteration: 5
setImmediate iteration: 5
nextTick iteration: 6
setImmediate iteration: 6
nextTick iteration: 7
setImmediate iteration: 7
nextTick iteration: 8
setImmediate iteration: 8
nextTick iteration: 9
setImmediate iteration: 9现在,让我们将对step的递归调用移到nextTick处理程序中,而不是setImmediate中。
function step(iteration) {
if (iteration === 10) return;
setImmediate(() => {
console.log(`setImmediate iteration: ${iteration}`);
});
process.nextTick(() => {
console.log(`nextTick iteration: ${iteration}`);
step(iteration + 1); // Recursive call from nextTick handler.
});
}
step(0);现在,我们已经将对step的递归调用移到了nextTick处理程序中,事情将以不同的顺序进行。事件循环的第一次迭代运行并调用step,同时注册一个setImmedaite处理程序和一个nextTick处理程序。在当前操作结束后,我们的nextTick处理程序将触发,它递归地调用step并注册另一个setImmediate处理程序和另一个nextTick处理程序。由于nextTick处理程序在当前操作之后触发,因此在nextTick处理程序中注册nextTick处理程序将导致第二个处理程序在当前处理程序操作完成后立即运行。nextTick处理程序将继续触发,防止当前事件循环继续。在看到单个nextTick处理程序触发之前,我们将完成所有的setImmediate处理程序。
上述代码的输出结果为:
nextTick iteration: 0
nextTick iteration: 1
nextTick iteration: 2
nextTick iteration: 3
nextTick iteration: 4
nextTick iteration: 5
nextTick iteration: 6
nextTick iteration: 7
nextTick iteration: 8
nextTick iteration: 9
setImmediate iteration: 0
setImmediate iteration: 1
setImmediate iteration: 2
setImmediate iteration: 3
setImmediate iteration: 4
setImmediate iteration: 5
setImmediate iteration: 6
setImmediate iteration: 7
setImmediate iteration: 8
setImmediate iteration: 9请注意,如果我们没有中断递归调用并在10次迭代后中止它,那么nextTick调用将继续递归,并且永远不会让事件循环继续到下一个阶段。这就是nextTick在递归使用时会变得阻塞的原因,而setImmediate将在下一个事件循环中触发,并且从一个事件循环中设置另一个setImmediate处理程序根本不会中断当前事件循环,从而允许它继续正常执行事件循环的各个阶段。
希望这能有所帮助!
PS -我同意其他评论者的观点,这两个函数的名称可以很容易地交换,因为nextTick听起来像是要在下一个事件循环中触发,而不是当前循环的结束,并且当前循环的结束比下一个循环的开始更“直接”。哦,这就是随着API的成熟和人们开始依赖现有接口而得到的结果。
https://stackoverflow.com/questions/15349733
复制相似问题