本文翻译自:setImmediate vs. nextTick
Node.js version 0.10 was released today and introduced setImmediate
. Node.js版本0.10已于今天发布,并引入了setImmediate
。 The API changes documentation suggests using it when doing recursive nextTick
calls. API更改文档建议在进行递归nextTick
调用时使用它。
From what MDN says it seems very similar to process.nextTick
. 从MDN看来,这似乎与process.nextTick
非常相似。
When should I use nextTick
and when should I use setImmediate
? 什么时候应该使用nextTick
当我应该使用setImmediate
?
#1楼
参考:https://stackoom.com/question/12PAL/setImmediate与nextTick
#2楼
Use setImmediate
if you want to queue the function behind whatever I/O event callbacks that are already in the event queue. 如果要将函数放在事件队列中已经存在的任何I / O事件回调后面,请使用setImmediate
。 Use process.nextTick
to effectively queue the function at the head of the event queue so that it executes immediately after the current function completes. 使用process.nextTick
有效地将函数放在事件队列的开头,以便在当前函数完成后立即执行。
So in a case where you're trying to break up a long running, CPU-bound job using recursion, you would now want to use setImmediate
rather than process.nextTick
to queue the next iteration as otherwise any I/O event callbacks wouldn't get the chance to run between iterations. 因此,在您尝试使用递归分解长时间运行且受CPU限制的作业的情况下,您现在想使用setImmediate
而不是process.nextTick
将下一个迭代排队,因为否则所有I / O事件回调都不会没有机会在迭代之间运行。
#3楼
In the comments in the answer, it does not explicitly state that nextTick shifted from Macrosemantics to Microsemantics. 在答案的注释中,它没有明确声明nextTick从Macrosemantics转变为Microsemantics。
before node 0.9 (when setImmediate was introduced), nextTick operated at the start of the next callstack. 在节点0.9之前(引入setImmediate时),nextTick在下一个调用堆栈的开始处运行。
since node 0.9, nextTick operates at the end of the existing callstack, whereas setImmediate is at the start of the next callstack 从节点0.9开始,nextTick在现有调用栈的末尾运行,而setImmediate在下一个调用栈的末尾运行
check out https://github.com/YuzuJS/setImmediate for tools and details 查看https://github.com/YuzuJS/setImmediate以获取工具和详细信息
#4楼
As an illustration 作为说明
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);
});
});
will give the following output 将给出以下输出
Start
Done
NT1
NT2
NT3
TO1
IO2
IO3
IM1
IM2
IM3
IM4
IO1
I hope this can help to understand the difference. 我希望这可以帮助理解差异。
#5楼
I think I can illustrate this quite nicely. 我想我可以很好地说明这一点。 Since nextTick
is called at the end of the current operation, calling it recursively can end up blocking the event loop from continuing. 由于nextTick
在当前操作结束时被调用,因此以递归方式调用它可能最终阻止事件循环继续进行。 setImmediate
solves this by firing in the check phase of the event loop, allowing event loop to continue normally. setImmediate
通过在事件循环的检查阶段触发来解决此问题,从而允许事件循环正常继续。
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
source: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/ 来源: https : //nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
Notice that the check phase is immediately after the poll phase. 请注意,检查阶段紧接在轮询阶段之后。 This is because the poll phase and I/O callbacks are the most likely places your calls to setImmediate
are going to run. 这是因为轮询阶段和I / O回调是您对setImmediate
的调用最有可能运行的地方。 So ideally most of those calls will actually be pretty immediate, just not as immediate as nextTick
which is checked after every operation and technically exists outside of the event loop. 因此,理想情况下,这些调用中的大多数实际上实际上是立即调用的,而不是像nextTick
那样立即nextTick
, nextTick
在每次操作后都要检查,并且从技术上讲是存在于事件循环之外的。
Let's take a look at a little example of the difference between setImmediate
and process.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);
Let's say we just ran this program and are stepping through the first iteration of the event loop. 假设我们只是运行了该程序,并逐步完成了事件循环的第一次迭代。 It will call into the step
function with iteration zero. 它将以零迭代次数调用step
函数。 It will then register two handlers, one for setImmediate
and one for process.nextTick
. 然后它将注册两个处理程序,一个用于setImmediate
,一个用于process.nextTick
。 We then recursively call this function from the setImmediate
handler which will run in the next check phase. 然后,我们从setImmediate
处理程序中递归调用此函数,该处理程序将在下一个检查阶段运行。 The nextTick
handler will run at the end of the current operation interrupting the event loop, so even though it was registered second it will actually run first. nextTick
处理程序将在当前操作结束时运行,中断事件循环,因此,即使它已被第二次注册,它实际上也会首先运行。
The order ends up being: nextTick
fires as current operation ends, next event loop begins, normal event loop phases execute, setImmediate
fires and recursively calls our step
function to start the process all over again. 最终的顺序是:当前操作结束时将触发nextTick
,下一个事件循环将开始,正常事件循环阶段将执行, setImmediate
触发并递归调用我们的step
函数以重新开始该过程。 Current operation ends, nextTick
fires, etc. 当前操作结束, nextTick
触发等。
The output of the above code would be: 上面代码的输出为:
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
Now let's move our recursive call to step
into our nextTick
handler instead of the setImmediate
. 现在,让我们继续我们的递归调用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);
Now that we have moved the recursive call to step
into the nextTick
handler things will behave in a different order. 现在我们已经搬到了递归调用step
到nextTick
处理事情会以不同的顺序行为。 Our first iteration of the event loop runs and calls step
registering a setImmedaite
handler as well as a nextTick
handler. 事件循环的第一个迭代运行并调用step
注册setImmedaite
处理程序以及nextTick
处理程序。 After the current operation ends our nextTick
handler fires which recursively calls step
and registers another setImmediate
handler as well as another nextTick
handler. 当前操作结束后,将触发nextTick
处理程序,该处理程序递归调用step
并注册另一个setImmediate
处理程序以及另一个nextTick
处理程序。 Since a nextTick
handler fires after the current operation, registering a nextTick
handler within a nextTick
handler will cause the second handler to run immediately after the current handler operation finishes. 由于nextTick
处理火灾当前操作之后,登记nextTick
一个内处理程序nextTick
处理程序将导致第二处理程序,以当前的处理程序的操作完成后立即运行。 The nextTick
handlers will keep firing, preventing the current event loop from ever continuing. nextTick
处理程序将继续触发,从而防止当前事件循环继续进行。 We will get through all our nextTick
handlers before we see a single setImmediate
handler fire. 在看到单个setImmediate
处理程序启动之前,我们将遍历所有nextTick
处理程序。
The output of the above code ends up being: 上面代码的输出最终是:
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
Note that had we not interrupted the recursive call and aborted it after 10 iterations then the nextTick
calls would keep recursing and never letting the event loop continue to the next phase. 请注意,如果我们没有中断递归调用并在10次迭代后中止它,则nextTick
调用将继续递归,并且永远不会让事件循环继续进行到下一个阶段。 This is how nextTick
can become blocking when used recursively whereas setImmediate
will fire in the next event loop and setting another setImmediate
handler from within one won't interrupt the current event loop at all, allowing it to continue executing phases of the event loop as normal. 这就是nextTick
在递归使用时会变为阻塞的方式,而setImmediate
将在下一个事件循环中触发,并且从一个内部设置另一个setImmediate
处理程序根本不会中断当前事件循环,从而使其能够照常继续执行事件循环的各个阶段。
Hope that helps! 希望有帮助!
PS - I agree with other commenters that the names of the two functions could easily be swapped since nextTick
sounds like it's going to fire in the next event loop rather than the end of the current one, and the end of the current loop is more "immediate" than the beginning of the next loop. PS-我同意其他评论者的观点,这两个函数的名称可以轻松交换,因为nextTick
听起来像是在下一个事件循环中触发,而不是在当前事件的末尾触发,而当前循环的结束更像是“立即”,而不是下一个循环的开始。 Oh well, that's what we get as an API matures and people come to depend on existing interfaces. 哦,这就是随着API的成熟以及人们开始依赖现有接口而获得的。
#6楼
In simple terms, process.NextTick() would executed at next tick of event loop. 简单来说,process.NextTick()将在事件循环的下一个计时执行。 However, the setImmediate, basically has a separate phase which ensures that the callback registered under setImmediate() will be called only after the IO callback and polling phase. 但是,setImmediate本质上具有一个单独的阶段,该阶段确保仅在IO回调和轮询阶段之后才调用在setImmediate()下注册的回调。
Please refer to this link for nice explanation: https://medium.com/the-node-js-collection/what-you-should-know-to-really-understand-the-node-js-event-loop-and-its-metrics-c4907b19da4c 请参考此链接以获得更好的解释: https : //medium.com/the-node-js-collection/what-you-should-know-to-really-understand-the-node-js-event-loop-and -its-metrics-c4907b19da4c