Answers:
setImmediate
如果要将函数放在事件队列中已存在的所有I / O事件回调后面,请使用此函数。用于process.nextTick
将函数有效地放在事件队列的开头,以便在当前函数完成后立即执行。
因此,在您尝试使用递归分解长时间运行且受CPU限制的作业的情况下,您现在想使用setImmediate
而不是process.nextTick
将下一个迭代放入队列中,因为否则任何I / O事件回调都不会获得机会在迭代之间运行。
requestAnimationFrame
因为它并不总是发生(我已经明确地看到了,我认为示例不是选项卡不是当前选项卡),并且可以在页面完成绘制之前调用它(即浏览器仍在忙于绘制)。
作为说明
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('TO1'), 0);
setImmediate(() => console.log('IM1'));
process.nextTick(() => console.log('NT1'));
setImmediate(() => console.log('IM2'));
process.nextTick(() => console.log('NT2'));
http.get(options, () => console.log('IO1'));
fs.readdir(process.cwd(), () => console.log('IO2'));
setImmediate(() => console.log('IM3'));
process.nextTick(() => console.log('NT3'));
setImmediate(() => console.log('IM4'));
fs.readdir(process.cwd(), () => console.log('IO3'));
console.log('Done');
setTimeout(done, 1500);
});
});
将给出以下输出
Start
Done
NT1
NT2
NT3
TO1
IO2
IO3
IM1
IM2
IM3
IM4
IO1
我希望这可以帮助理解差异。
更新:
process.nextTick()
在激发任何其他I / O事件之前,将运行延迟回叫,而对于setImmediate(),则将执行排队在队列中已存在的任何I / O事件之后。Mario Casciaro撰写的Node.js设计模式(可能是有关node.js / js的最好的书)
For example, if we run the following script which is not within an I/O cycle (i.e. the main module), the order in which the two timers are executed is non-deterministic, as it is bound by the performance of the process:
所以,这个回答并没有真正回答确切的差异,但仅仅是一个例子,可以在不同的环境而改变
我想我可以很好地说明这一点。由于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的成熟以及人们开始依赖现有接口而获得的。
在答案的注释中,它没有明确声明nextTick从Macrosemantics转变为Microsemantics。
在节点0.9之前(引入setImmediate时),nextTick在下一个调用堆栈的开始处运行。
从节点0.9开始,nextTick在现有调用栈的末尾运行,而setImmediate在下一个调用栈的末尾运行
查看https://github.com/YuzuJS/setImmediate以获取工具和详细信息
简单来说,process.NextTick()将在事件循环的下一个计时执行。但是,setImmediate本质上具有一个单独的阶段,该阶段确保仅在IO回调和轮询阶段之后才调用在setImmediate()下注册的回调。
请参考此链接以获得更好的解释:https : //medium.com/the-node-js-collection/what-you-should-know-to-really-understand-the-node-js-event-loop-and -its-metrics-c4907b19da4c
这里有一些很好的答案,详细介绍了它们如何工作。
只需添加一个回答特定问题的答案即可:
什么
nextTick
时候应该使用setImmediate
?什么时候应该使用?
setImmediate
。在Node.js的事件循环,定时器和process.nextTick()
文档包括以下内容:
我们建议开发人员
setImmediate()
在所有情况下都使用它,因为这样更容易推理(并且导致代码与更广泛的环境兼容,例如浏览器JS。)
在文档的早期,它警告process.nextTick
可能导致...
有些糟糕的情况,因为它允许您通过进行递归
process.nextTick()
调用来“饿死”您的I / O,从而防止事件循环到达轮询阶段。
事实证明,process.nextTick
甚至可以饿死Promises
:
Promise.resolve().then(() => { console.log('this happens LAST'); });
process.nextTick(() => {
console.log('all of these...');
process.nextTick(() => {
console.log('...happen before...');
process.nextTick(() => {
console.log('...the Promise ever...');
process.nextTick(() => {
console.log('...has a chance to resolve');
})
})
})
})
另一方面,setImmediate
“ 更易于推理 ”并避免了以下类型的问题:
Promise.resolve().then(() => { console.log('this happens FIRST'); });
setImmediate(() => {
console.log('this happens LAST');
})
因此,除非特别需要的独特行为,否则process.nextTick
建议的方法是“ 在所有情况下使用setImmediate()
”。
我建议您检查文档专用于Loop的部分,以更好地理解。从那里摘录的一些片段:
就用户而言,我们有两个呼叫类似,但是它们的名称令人困惑。
process.nextTick()在同一阶段立即触发
setImmediate()在
事件循环的以下迭代或“滴答”中触发
本质上,名称应互换。process.nextTick()比setImmediate()更快地触发,但这是过去的产物,不太可能改变。