nextTick vs setImmediate,直观解释

时间:2013-07-06 12:24:12

标签: node.js

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

来源:

5 个答案:

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

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

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

请看这个视频,其中Philip Roberts给出了关于运行时事件循环的一个很好的解释,并查看这个在线eventloop调试器Live test how event loop works

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

答案 2 :(得分:8)

我无法重现setImmediate的结果。它应该与nextTick相同(并且在我的测试中),因为在这种情况下它们几乎完全相同。唯一合理的解释是setImmediate在某种程度上是同步的,但事实并非如此。

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

答案 3 :(得分:3)

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

setImmediate

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

    1. 这是NodeJS的进程全局对象功能。
    2. 在事件循环继续之前,将解析传递给process.nextTick()的所有回调。
    3. 允许用户处理错误。
    4. 帮助在事件循环继续之前再次尝试请求。
    5. 简单代码片段。

      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]

第二次迭代

Aqueue撤出以执行

在执行期间,它将BE置于queue然后,A完成并开始下一次迭代

检查队列:[B,E]

第三次迭代

拉出B并推送C D

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

第四次迭代

拉出E并推送F G

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

<强>最后

按顺序执行队列中的回调

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

不同的是:

  

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

要清楚,事件循环维护多个队列,而check queue只是其中之一,节点将根据某些规则决定使用哪个队列

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