我对nextTick和setImmediate之间的差异感到非常困惑。我已经在互联网上阅读了有关它们的所有文档,但我仍然不明白它们是如何工作的。
示例:
function log(n) { console.log(n); }
setImmediate
setImmediate(function() {
setImmediate(function() {
log(1);
setImmediate(function() { log(2); });
setImmediate(function() { log(3); });
});
setImmediate(function() {
log(4);
setImmediate(function() { log(5); });
setImmediate(function() { log(6); });
});
});
//1 2 3 4 5 6
nextTick
process.nextTick(function() {
process.nextTick(function() {
log(1);
process.nextTick(function() { log(2); });
process.nextTick(function() { log(3); });
});
process.nextTick(function() {
log(4);
process.nextTick(function() { log(5); });
process.nextTick(function() { log(6); });
});
});
//1 4 2 3 5 6
为什么这些结果?请用视觉或非常容易理解的方式解释。甚至节点核心开发人员也不同意人们应该如何理解nextTick和setImmediate。
来源:
答案 0 :(得分:100)
考虑以下两个例子:
<强> setImmediate 强>
setImmediate(function A() {
setImmediate(function B() {
log(1);
setImmediate(function D() { log(2); });
setImmediate(function E() { log(3); });
});
setImmediate(function C() {
log(4);
setImmediate(function F() { log(5); });
setImmediate(function G() { log(6); });
});
});
setTimeout(function timeout() {
console.log('TIMEOUT FIRED');
}, 0)
// 'TIMEOUT FIRED' 1 4 2 3 5 6
// OR
// 1 'TIMEOUT FIRED' 4 2 3 5 6
<强> nextTick 强>
process.nextTick(function A() {
process.nextTick(function B() {
log(1);
process.nextTick(function D() { log(2); });
process.nextTick(function E() { log(3); });
});
process.nextTick(function C() {
log(4);
process.nextTick(function F() { log(5); });
process.nextTick(function G() { log(6); });
});
});
setTimeout(function timeout() {
console.log('TIMEOUT FIRED');
}, 0)
// 1 4 2 3 5 6 'TIMEOUT FIRED'
setImmediate回调是在事件循环中触发的,每次迭代按照它们排队的顺序触发一次。因此,在事件循环的第一次迭代中,将触发回调A.然后在事件循环的第二次迭代中,触发回调B,然后在事件循环的第三次迭代中触发回调C等。这可以防止事件循环被阻塞并允许其他I / O或计时器回调在平均时间内调用(就像0ms超时的情况一样,在第一次或第二次循环迭代时触发)。
但是,nextTick回调总是在当前代码执行完毕后立即触发,然后返回事件循环。在nextTick示例中,我们在返回事件循环之前最终执行所有nextTick回调。由于setTimeout的回调将从事件循环中调用,因此在我们完成每个nextTick回调之前,不会输出文本'TIMEOUT FIRED'。
答案 1 :(得分:22)
根据Node.js doc这两个函数的名称是exactly swapped
setImmediate()( BEST RECOMMENDED )
首先在事件队列中点火
process.nextTick()(特殊情况的使用见示例)
立即开火,有点在当前档案的最后写一份声明
如果我们有这个代码
setTimeout(function(){
console.log('Hello world 5'); // It's waiting like a normal person at a queue
}, 0);
setImmediate(function(){
console.log('Hello world 4');
// It's like get to last and be take care of first
// but always after of .nextTick and before of setInterval(, 0)
});
process.nextTick(function(){
console.log('Hello world 3'); // It's like be at the bottom at this file
});
console.log('Hello world 1');
console.log('Hello world 2');
根据您的要求进行直观说明:
在处理它之前必须发出和事件时使用process.nextTick()的情况:
const EventEmitter = require('events');
const util = require('util');
function MyEmitter() {
EventEmitter.call(this);
// use nextTick to emit the event once a handler is assigned
process.nextTick(function () {
this.emit('event');
}.bind(this));
}
util.inherits(MyEmitter, EventEmitter);
const myEmitter = new MyEmitter();
myEmitter.on('event', function() {
console.log('an event occurred!');
});
请看这个视频,其中Philip Roberts给出了关于运行时事件循环的一个很好的解释,并查看这个在线eventloop调试器Live test how event loop works
答案 2 :(得分:8)
我无法重现setImmediate
的结果。它应该与nextTick
相同(并且在我的测试中),因为在这种情况下它们几乎完全相同。唯一合理的解释是setImmediate
在某种程度上是同步的,但事实并非如此。
根据NodeJS documentation,唯一真正的区别是多个nextTick
可以在一个循环迭代中触发(取决于maxTickDepth
),而setImmediate
每次迭代触发一次。
答案 3 :(得分:3)
下面给出了更清晰的信息。
require
的情况下调用它们。 console.log("I'm First");
setImmediate(function () {
console.log('Im setImmediate');
});
console.log("I'm Second");
process.nextTick(function () {
console.log('Im nextTick');
});
console.log("I'm Last");
/*
Output
$ node server.js
I'm First
I'm Second
I'm Last
Im nextTick
Im setImmediate
*/
答案 4 :(得分:2)
我认为上面的所有答案都已过时,因为我使用当前版本的nodejs不断得到不同的答案,并且很容易推理
var log=console.log
log(process.version)
var makeAsyncCall
if(false)
makeAsyncCall=setImmediate
else
makeAsyncCall=process.nextTick;
makeAsyncCall(function A () {
makeAsyncCall(function B() {
log(1);
makeAsyncCall(function C() { log(2); });
makeAsyncCall(function D() { log(3); });
});
makeAsyncCall(function E() {
log(4);
makeAsyncCall(function F() { log(5); });
makeAsyncCall(function G() { log(6); });
});
});
//1
//4
//2
//3
//5
//6
//in both case
阅读https://github.com/nodejs/node/blob/master/doc/topics/the-event-loop-timers-and-nexttick.md#processnexttick-vs-setimmediate后
让我们使用从setImmediate
开始,我们应该跟踪check queue
,因为它是setImmediate
回调所在的位置。
第一次迭代
A
推送到check queue
检查队列:[A]
第二次迭代
A
从queue
撤出以执行
在执行期间,它将B
和E
置于queue
然后,A
完成并开始下一次迭代
检查队列:[B,E]
第三次迭代
拉出B
并推送C
D
检查队列:[E,C,D]
第四次迭代
拉出E
并推送F
G
检查队列:[C,D,F,G]
<强>最后强>
按顺序执行队列中的回调
对于nextTick
情况,队列的工作方式完全相同,这就是产生相同结果的原因
不同的是:
在当前操作之后将处理nextTickQueue 完成,无论事件循环的当前阶段如何
要清楚,事件循环维护多个队列,而check queue
只是其中之一,节点将根据某些规则决定使用哪个队列
process.nextTick
然而,它有点绕过所有规则并立即执行nextTick
中的回调