93

我对 nextTick 和 setImmediate 之间的区别感到非常困惑。我已经在互联网上阅读了所有关于它们的文档,但我仍然不明白它们是如何工作的。

例子:

function log(n) { console.log(n); }

立即设置

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

下一个勾选

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。

资料来源:

4

5 回答 5

106

考虑以下两个示例:

立即设置

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

下一个勾选

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

于 2013-11-30T04:37:40.970 回答
25

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

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

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

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

于 2016-08-03T11:49:55.073 回答
8

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

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

于 2013-07-06T13:18:49.340 回答
6

下面给你更好的清晰度。

立即设置

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

下一个勾选

  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
*/
于 2017-01-30T02:21:45.607 回答
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执行

在它的执行过程中,它把BEqueue,然后,A完成并开始下一次迭代

检查队列:[B,E]

第三次迭代

拉出B并推C D

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

第四次迭代

拉出E并推F G

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

最后

按顺序执行队列中的回调

例如nextTick,队列的工作方式完全相同,这就是它产生相同结果的原因

不同的是:

nextTickQueue 将在当前操作完成后处理,与事件循环的当前阶段无关

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

但是,process.nextTick它有点绕过所有规则并nextTick立即执行回调

于 2016-09-09T10:37:45.373 回答