【问题标题】:nextTick vs setImmediate, visual explanationnextTick vs setImmediate,直观解释
【发布时间】:2013-07-04 09:17:23
【问题描述】:

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

来源:

【问题讨论】:

  • 好问题。这是说明差异的绝佳方式。
  • 两次执行的输出相同 (1 4 2 3 5 6) (Node v5.6.0)
  • 明智而有帮助的问题!
  • 两者产生相同的输出
  • 看看那一年,5年前!很多事情本可以改变。

标签: node.js


【解决方案1】:

考虑以下两个例子:

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 超时的情况一样,在第 1 次或第 2 次循环迭代时触发)。

然而,nextTick 回调总是在当前代码执行完成后和返回事件循环之前立即触发。在 nextTick 示例中,我们最终会在返回事件循环之前执行所有 nextTick 回调。由于 setTimeout 的回调将从事件循环中调用,因此在我们完成每个 nextTick 回调之前,不会输出文本“TIMEOUT FIRED”。

【讨论】:

  • 完美,最后一段是理解主要区别的关键。
  • 你的 nextTick 例子有不同的输出:TIMEOUT FIRED 1 4 2 3 5 6
  • 你运行的是什么版本的节点?在节点 0.10.x 上,您应该始终在触发事件循环之前运行 nextTick 回调。
  • 我希望他们选择一个不同的名字。现在“nextTick”比 setImmediate 更直接。
  • @StrugglingCoder 因为当 A 执行时,它将 B 和 C 设置为下一个调度。当 B 执行时,它将 D 和 E 设置为下一个调度,但 C 已被调度在 B 之后。请记住,调度或注册回调的时间与实际触发回调的时间是有区别的。
【解决方案2】:

根据这两个函数的Node.js doc名称是exactly swapped

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

根据您的要求提供视觉解释:

当你必须发出和事件之前使用 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给我们一个关于运行时事件循环的很好的解释,看看这个在线事件循环调试器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

【讨论】:

  • 这个答案更有意义!谢谢@gsalgadotoledo :p 伙计,写你的名字比理解 nextTick 和 setImmediate 更难。
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    以下内容更清楚。

    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
    */
    

    【讨论】:

      【解决方案5】:

      我认为上面所有的答案都是过时的,因为我用当前版本的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并推入CD

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

      第四次迭代

      拉出E并推入FG

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

      终于

      按顺序执行队列中的回调

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

      不同的是:

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

      需要明确的是,事件循环维护多个队列,check queue只是其中之一,节点会根据一些规则决定使用哪个队列

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

      【讨论】:

        猜你喜欢
        • 2023-03-19
        • 2015-02-23
        • 2014-04-26
        • 2020-10-06
        • 2021-10-26
        • 2018-06-16
        • 2015-02-23
        相关资源
        最近更新 更多