node.js - nextTick vs setImmediate,可视化解释

我对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。

资料来源:

  • setImmediate与nextTick
  • 为什么setImmediate比nextTick慢得多?
  • setImmediate并不总是很直接
Gabriel Llamas asked 2019-09-10T09:08:08Z
5个解决方案
99 votes

考虑以下两个例子:

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'。

Dave Stibrany answered 2019-09-10T09:08:38Z
22 votes

根据Node.js doc这两个函数的名称是完全交换的

setImmediate()(推荐最佳)

它首先在事件队列中着火


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');

根据您的要求进行直观说明:

enter image description here

使用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!');
});

看看这个视频菲利普罗伯茨给我们一个关于运行时事件循环的一个很好的解释,看看这个在线eventloop调试器实时测试事件循环如何工作

资源:[https://github.com/nodejs/node/blob/master/doc/topics/the-event-loop-timers-and-nexttick.md#processnexttick-vs-setimmediate]

gsalgadotoledo answered 2019-09-10T09:10:04Z
8 votes

我不能复制你的nextTick的结果。它应该与maxTickDepth相同(并且它在我的测试中),因为在这种情况下他们做了几乎相同的事情。 对此的唯一合理解释是setImmediate在某种程度上是同步的,但事实并非如此。

根据NodeJS文档,唯一真正的区别是多个nextTick可能在一次循环迭代中触发(取决于maxTickDepth),而setImmediate每次迭代触发一次。

freakish answered 2019-09-10T09:10:37Z
3 votes

下面给出了更清晰的信息。

setImmediate

  1. 一旦当前轮询阶段完成,它就会执行一个脚本。
  2. 它是一个定时器模块功能,定时器功能是全局的,您可以在没有require的情况下调用它们。
  3. 它可以通过clearImmediate()清除。
  4. 在setTimeout()和setInterval()之前的I / O事件回调之后设置回调的“立即”执行。

nextTick

  1. 它是NodeJS的进程全局对象函数。
  2. 传递给process.nextTick()的所有回调都将在事件循环继续之前得到解决。
  3. 允许用户处理错误。
  4. 在事件循环继续之前帮助再次尝试请求。

简单的代码片段。

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
*/
Venkat.R answered 2019-09-10T09:12:04Z
2 votes

我认为上面的所有答案都已经过时了,因为我不断地使用当前版本的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]后让我们使用从process.nextTick开始我们应该跟踪nextTick,因为它是G回调所在的位置。

第一次迭代

process.nextTick被推送至nextTick

检查队列:[A]

第二次迭代

process.nextTicknextTick拉出来执行

在执行期间,它将process.nextTicknextTick放入G然后,A完成并开始下一次迭代

检查队列:[B,E]

第三次迭代

拔出process.nextTick并推nextTick G

检查队列:[E,C,D]

第四次迭代

拔出process.nextTick并推nextTick G

检查队列:[C,D,F,G]

最后

按顺序执行队列中的回调

对于process.nextTick情况,队列的工作方式完全相同,这就是它产生相同结果的原因

不同的是:

在当前操作之后将处理nextTickQueue   无论事件循环的当前阶段如何,都会完成

要清楚,事件循环保持多个队列,process.nextTick只是其中之一,节点将根据某些规则决定使用哪个队列

process.nextTick然而,它有点绕过所有规则并立即执行nextTick中的回调

Guichi answered 2019-09-10T09:14:44Z
translate from https://stackoverflow.com:/questions/17502948/nexttick-vs-setimmediate-visual-explanation