node.js setImmediate 与 nextTick
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15349733/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
setImmediate vs. nextTick
提问by Benjamin Gruenbaum
Node.js version 0.10 was released today and introduced setImmediate. The API changesdocumentation suggests using it when doing recursive nextTickcalls.
Node.js 0.10 版今天发布并引入了 setImmediate. 该API的变化文档建议做递归时使用它nextTick调用。
From what MDN saysit seems very similar to process.nextTick.
从MDN 的说法来看,它似乎与process.nextTick.
When should I use nextTickand when should I use setImmediate?
什么时候用nextTick,什么时候用setImmediate?
回答by JohnnyHK
Use setImmediateif you want to queue the function behind whatever I/O event callbacks that are already in the event queue. Use process.nextTickto effectively queue the function at the head of the event queue so that it executes immediately after the current function completes.
setImmediate如果要将函数排在事件队列中已有的任何 I/O 事件回调后面,请使用。用于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 setImmediaterather than process.nextTickto 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 事件回调都不会有机会在迭代之间运行。
回答by DraganS
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.
我希望这可以帮助理解差异。
Updated:
更新:
Callbacks deferred with
process.nextTick()run before any other I/O event is fired, while with setImmediate(), the execution is queued behind any I/O event that is already in the queue.Node.js Design Patterns, by Mario Casciaro (probably the best book about node.js/js)
process.nextTick()在触发任何其他 I/O 事件之前,使用run延迟回调,而使用 setImmediate(),执行会排在队列中已经存在的任何 I/O 事件之后。Node.js Design Patterns,作者 Mario Casciaro(可能是关于 node.js/js 的最好的书)
回答by Chev
I think I can illustrate this quite nicely. Since nextTickis called at the end of the current operation, calling it recursively can end up blocking the event loop from continuing. setImmediatesolves this by firing in the check phase of the event loop, allowing event loop to continue normally.
我想我可以很好地说明这一点。由于nextTick在当前操作结束时调用它,递归调用它最终会阻止事件循环继续。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 setImmediateare going to run. So ideally most of those calls will actually be pretty immediate, just not as immediate as nextTickwhich is checked after every operation and technically exists outside of the event loop.
请注意,检查阶段紧跟在轮询阶段之后。这是因为轮询阶段和 I/O 回调是您的调用最有可能setImmediate运行的地方。因此,理想情况下,大多数这些调用实际上是非常即时的,只是不像nextTick在每次操作后检查的那样即时,并且技术上存在于事件循环之外。
Let's take a look at a little example of the difference between setImmediateand 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 stepfunction with iteration zero. It will then register two handlers, one for setImmediateand one for process.nextTick. We then recursively call this function from the setImmediatehandler which will run in the next check phase. The nextTickhandler 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.
假设我们刚刚运行了这个程序并且正在逐步完成事件循环的第一次迭代。它将调用step迭代为零的函数。然后它将注册两个处理程序,一个 forsetImmediate和一个 for process.nextTick。然后我们从setImmediate将在下一个检查阶段运行的处理程序递归调用此函数。该nextTick处理器将在当前的运行中断事件循环结束时运行,所以即使它注册的第二它实际上将首先运行。
The order ends up being: nextTickfires as current operation ends, next event loop begins, normal event loop phases execute, setImmediatefires and recursively calls our stepfunction to start the process all over again. Current operation ends, nextTickfires, etc.
最终的顺序是:nextTick在当前操作结束时触发,下一个事件循环开始,正常的事件循环阶段执行,setImmediate触发并递归调用我们的step函数以重新开始整个过程。当前操作结束、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 stepinto our nextTickhandler 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 stepinto the nextTickhandler things will behave in a different order. Our first iteration of the event loop runs and calls stepregistering a setImmedaitehandler as well as a nextTickhandler. After the current operation ends our nextTickhandler fires which recursively calls stepand registers another setImmediatehandler as well as another nextTickhandler. Since a nextTickhandler fires after the current operation, registering a nextTickhandler within a nextTickhandler will cause the second handler to run immediately after the current handler operation finishes. The nextTickhandlers will keep firing, preventing the current event loop from ever continuing. We will get through all our nextTickhandlers before we see a single setImmediatehandler fire.
现在我们已经将递归调用移动step到nextTick处理程序中,事情将以不同的顺序运行。事件循环的第一次迭代运行并调用step注册setImmedaite处理程序和nextTick处理程序。当前操作结束后,我们的nextTick处理程序会触发,它会递归调用step并注册另一个setImmediate处理程序以及另一个nextTick处理程序。由于nextTick处理程序在当前操作之后触发,因此nextTick在nextTick处理程序中注册处理程序将导致第二个处理程序在当前处理程序操作完成后立即运行。该nextTick处理程序将持续开火,阻止当前事件循环从不断持续。我们将通过我们所有的nextTick在我们看到单个setImmediate处理程序触发之前。
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 nextTickcalls would keep recursing and never letting the event loop continue to the next phase. This is how nextTickcan become blocking when used recursively whereas setImmediatewill fire in the next event loop and setting another setImmediatehandler from within one won't interrupt the current event loop at all, allowing it to continue executing phases of the event loop as normal.
请注意,如果我们没有中断递归调用并在 10 次迭代后中止它,那么nextTick调用将继续递归并且永远不会让事件循环继续到下一阶段。这就是如何nextTick在递归使用时变得阻塞,而setImmediate将在下一个事件循环中触发,并且从一个事件循环中设置另一个setImmediate处理程序根本不会中断当前事件循环,允许它继续正常执行事件循环的各个阶段。
Hope that helps!
希望有帮助!
PS - I agree with other commenters that the names of the two functions could easily be swapped since nextTicksounds 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. Oh well, that's what we get as an API matures and people come to depend on existing interfaces.
PS - 我同意其他评论者的意见,这两个函数的名称可以很容易地交换,因为nextTick听起来它会在下一个事件循环而不是当前循环结束时触发,并且当前循环的结束更“立即” "而不是下一个循环的开始。哦,这就是我们随着 API 的成熟和人们开始依赖现有接口而得到的。
回答by Jay Day Zee
In the comments in the answer, it does not explicitly state that nextTick shifted from Macrosemantics to Microsemantics.
在答案的评论中,它没有明确说明 nextTick 从宏语义转向微语义。
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/setImmediatefor tools and details
查看https://github.com/YuzuJS/setImmediate获取工具和详细信息
回答by GingerBeer
In simple terms, process.NextTick() would executed at next tick of event loop. 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.
简单来说, process.NextTick() 将在事件循环的下一个滴答处执行。但是,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
回答by Brian Adams
Some great answers here detailing how they both work.
这里有一些很好的答案,详细说明了它们是如何工作的。
Just adding one that answers the specific question asked:
只需添加一个回答特定问题的答案:
When should I use
nextTickand when should I usesetImmediate?
什么时候用
nextTick,什么时候用setImmediate?
Always use setImmediate.
始终使用setImmediate.
The Node.js Event Loop, Timers, and process.nextTick()doc includes the following:
在Node.js的事件循环,定时器和process.nextTick()文档包括以下内容:
We recommend developers use
setImmediate()in all cases because it's easier to reason about (and it leads to code that's compatible with a wider variety of environments, like browser JS.)
我们建议开发人员
setImmediate()在所有情况下使用,因为它更容易推理(并且它导致代码与更广泛的环境兼容,例如浏览器 JS。)
Earlier in the doc it warns that process.nextTickcan lead to...
在文档的早些时候,它警告说process.nextTick可能会导致......
some bad situations because it allows you to "starve" your I/O by making recursive
process.nextTick()calls, which prevents the event loop from reaching the pollphase.
一些糟糕的情况,因为它允许您通过递归
process.nextTick()调用“饿死”您的 I/O,从而防止事件循环到达轮询阶段。
As it turns out, process.nextTickcan even starve Promises:
事实证明,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');
})
})
})
})
On the other hand, setImmediateis "easier to reason about" and avoids these types of issues:
另一方面,setImmediate“更容易推理”并避免了这些类型的问题:
Promise.resolve().then(() => { console.log('this happens FIRST'); });
setImmediate(() => {
console.log('this happens LAST');
})
So unless there is a specific need for the unique behavior of process.nextTick, the recommended approach is to "use setImmediate()in all cases".
因此,除非对 的独特行为有特定需求,否则process.nextTick推荐的方法是“在所有情况下使用setImmediate()”。
回答by Valikhan Akhmedov
I recommend you to check docssection dedicated for Loop to get better understanding. Some snippet taken from there:
我建议您查看专用于 Loop 的文档部分以获得更好的理解。从那里截取的一些片段:
We have two calls that are similar as far as users are concerned, but their names are confusing.
就用户而言,我们有两个相似的调用,但它们的名称令人困惑。
process.nextTick() fires immediately on the same phase
setImmediate() fires on the following iteration or 'tick' of the
event loop
process.nextTick() 在同一阶段立即触发
setImmediate() 在
事件循环的下一次迭代或“滴答”时触发
In essence, the names should be swapped. process.nextTick() fires more immediately than setImmediate(), but this is an artifact of the past which is unlikely to change.
本质上,名称应该交换。process.nextTick() 比 setImmediate() 更直接地触发,但这是过去的产物,不太可能改变。


