Javascript setImmediate vs.nextTick

Javascript setImmediate vs.nextTick,javascript,node.js,setimmediate,Javascript,Node.js,Setimmediate,Node.js版本0.10今天发布,并引入了setImmediate。文档建议在执行递归nextTick调用时使用它 从它看起来非常类似于进程.nextTick 如果要在事件队列中已存在的任何I/O事件回调之后对函数进行排队,我应该在何时使用下一个回调以及何时使用setImmediate?使用setImmediate。使用process.nextTick将函数有效地排在事件队列的最前面,以便在当前函数完成后立即执行 因此,如果您试图使用递归分解一个长时间运行、CPU受限的作业,您现在可能希望使用

Node.js版本0.10今天发布,并引入了
setImmediate
。文档建议在执行递归
nextTick
调用时使用它

从它看起来非常类似于
进程.nextTick


如果要在事件队列中已存在的任何I/O事件回调之后对函数进行排队,我应该在何时使用下一个回调以及何时使用setImmediate?

使用
setImmediate
。使用
process.nextTick
将函数有效地排在事件队列的最前面,以便在当前函数完成后立即执行


因此,如果您试图使用递归分解一个长时间运行、CPU受限的作业,您现在可能希望使用
setImmediate
而不是
process.nextTick
对下一次迭代进行排队,否则任何I/O事件回调都无法在两次迭代之间运行。

在回答中的注释中,它没有明确说明nextTick从宏观语义转移到微观语义

在节点0.9之前(当引入setImmediate时),nextTick在下一个调用堆栈的开始处运行

从节点0.9开始,nextTick在现有调用堆栈的末尾运行,而setImmediate在下一个调用堆栈的开头

查看工具和详细信息,如图所示

import fs from 'fs';
import http from 'http';

const options = {
  host: 'www.stackoverflow.com',
  port: 80,
  path: '/index.html'
};

describe('deferredExecution', () => {
  it('deferredExecution', (done) => {
    console.log('Start');
    setTimeout(() => console.log('TO1'), 0);
    setImmediate(() => console.log('IM1'));
    process.nextTick(() => console.log('NT1'));
    setImmediate(() => console.log('IM2'));
    process.nextTick(() => console.log('NT2'));
    http.get(options, () => console.log('IO1'));
    fs.readdir(process.cwd(), () => console.log('IO2'));
    setImmediate(() => console.log('IM3'));
    process.nextTick(() => console.log('NT3'));
    setImmediate(() => console.log('IM4'));
    fs.readdir(process.cwd(), () => console.log('IO3'));
    console.log('Done');
    setTimeout(done, 1500);
  });
});
将给出以下输出

Start
Done
NT1
NT2
NT3
TO1
IO2
IO3
IM1
IM2
IM3
IM4
IO1
我希望这有助于理解两者的区别

更新:

使用
process.nextTick()
延迟的回调在任何其他I/O之前运行 事件被激发,而使用setImmediate()时,执行将排队 在队列中已存在的任何I/O事件之后

Node.js设计模式,由Mario Casciaro编写(可能是关于Node.js/js的最佳书籍)


我想我可以很好地说明这一点。由于在当前操作结束时调用了
nextTick
,因此递归调用它可能会阻止事件循环继续
setImmediate
通过在事件循环的检查阶段触发来解决此问题,允许事件循环正常继续

   ┌───────────────────────┐
┌─>│        timers         │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     I/O callbacks     │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     idle, prepare     │
│  └──────────┬────────────┘      ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │
│  │         poll          │<─────┤  connections, │
│  └──────────┬────────────┘      │   data, etc.  │
│  ┌──────────┴────────────┐      └───────────────┘
│  │        check          │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
└──┤    close callbacks    │
   └───────────────────────┘
假设我们刚刚运行了这个程序,正在逐步完成事件循环的第一次迭代。它将调用迭代为零的
步骤
函数。然后它将注册两个处理程序,一个用于
setImmediate
,另一个用于
process.nextTick
。然后,我们从
setImmediate
处理程序递归调用该函数,该处理程序将在下一个检查阶段运行。
nextTick
处理程序将在中断事件循环的当前操作结束时运行,因此,即使它是第二次注册,它实际上也将首先运行

顺序是:
nextTick
在当前操作结束时激发,下一个事件循环开始,正常事件循环阶段执行,
setImmediate
激发并递归调用我们的
step
函数重新启动流程。当前操作结束,
nextTick
点火等

上述代码的输出为:

nextTick iteration: 0
setImmediate iteration: 0
nextTick iteration: 1
setImmediate iteration: 1
nextTick iteration: 2
setImmediate iteration: 2
nextTick iteration: 3
setImmediate iteration: 3
nextTick iteration: 4
setImmediate iteration: 4
nextTick iteration: 5
setImmediate iteration: 5
nextTick iteration: 6
setImmediate iteration: 6
nextTick iteration: 7
setImmediate iteration: 7
nextTick iteration: 8
setImmediate iteration: 8
nextTick iteration: 9
setImmediate iteration: 9
现在,让我们将对
step
的递归调用移到
nextTick
处理程序中,而不是
setImmediate

function step(iteration) {
  if (iteration === 10) return;
  setImmediate(() => {
    console.log(`setImmediate iteration: ${iteration}`);
  });
  process.nextTick(() => {
    console.log(`nextTick iteration: ${iteration}`);
    step(iteration + 1); // Recursive call from nextTick handler.
  });
}
step(0);
现在,我们已经将对
步骤
的递归调用移动到
nextTick
处理程序中,事情将以不同的顺序运行。事件循环的第一次迭代运行并调用
step
注册
setimmdaite
处理程序以及
nextTick
处理程序。当前操作结束后,我们的
nextTick
处理程序将触发,该处理程序递归调用
step
,并注册另一个
setImmediate
处理程序以及另一个
nextTick
处理程序。由于
nextTick
处理程序在当前操作后激发,因此在
nextTick
处理程序中注册
nextTick
处理程序将导致第二个处理程序在当前处理程序操作完成后立即运行。
nextTick
处理程序将继续启动,阻止当前事件循环继续。在看到单个
setImmediate
处理程序触发之前,我们将完成所有
nextTick
处理程序

上述代码的输出结果是:

nextTick iteration: 0
nextTick iteration: 1
nextTick iteration: 2
nextTick iteration: 3
nextTick iteration: 4
nextTick iteration: 5
nextTick iteration: 6
nextTick iteration: 7
nextTick iteration: 8
nextTick iteration: 9
setImmediate iteration: 0
setImmediate iteration: 1
setImmediate iteration: 2
setImmediate iteration: 3
setImmediate iteration: 4
setImmediate iteration: 5
setImmediate iteration: 6
setImmediate iteration: 7
setImmediate iteration: 8
setImmediate iteration: 9
请注意,如果我们没有中断递归调用并在10次迭代后中止它,那么
nextTick
调用将继续递归,并且永远不会让事件循环继续到下一个阶段。这就是为什么
nextTick
在递归使用时会变成阻塞,而
setImmediate
将在下一个事件循环中触发,并且从一个事件循环中设置另一个
setImmediate
处理程序根本不会中断当前事件循环,从而允许它继续正常执行事件循环的各个阶段

希望有帮助


PS-我同意其他评论者的观点,即两个函数的名称可以很容易地互换,因为
nextTick
听起来好像它将在下一个事件循环中触发,而不是在当前循环的结束时触发,并且当前循环的结束比下一个循环的开始更“直接”。哦,好吧,随着API的成熟和人们对现有接口的依赖,这就是我们得到的结果。

简单地说,process.NextTick()将在事件循环的下一个滴答声中执行。但是,setImmediate基本上有一个单独的阶段,确保在setImmediate()下注册的回调仅在IO回调和轮询阶段之后调用

有关详细说明,请参阅此链接:

我建议您查看专门用于循环的部分,以便更好地理解。以下是其中的一些片段:

就用户而言,我们有两个电话是相似的,但他们的名字令人困惑

  • process.nextTick()在同一阶段立即激发

  • setImmediate()在
    nextTick iteration: 0
    nextTick iteration: 1
    nextTick iteration: 2
    nextTick iteration: 3
    nextTick iteration: 4
    nextTick iteration: 5
    nextTick iteration: 6
    nextTick iteration: 7
    nextTick iteration: 8
    nextTick iteration: 9
    setImmediate iteration: 0
    setImmediate iteration: 1
    setImmediate iteration: 2
    setImmediate iteration: 3
    setImmediate iteration: 4
    setImmediate iteration: 5
    setImmediate iteration: 6
    setImmediate iteration: 7
    setImmediate iteration: 8
    setImmediate iteration: 9
    
    Promise.resolve().then(() => { console.log('this happens LAST'); });
    
    process.nextTick(() => {
      console.log('all of these...');
      process.nextTick(() => {
        console.log('...happen before...');
        process.nextTick(() => {
          console.log('...the Promise ever...');
          process.nextTick(() => {
            console.log('...has a chance to resolve');
          })
        })
      })
    })
    
    Promise.resolve().then(() => { console.log('this happens FIRST'); });
    
    setImmediate(() => {
      console.log('this happens LAST');
    })
    
    const nt_recursive = () => process.nextTick(nt_recursive);
    nt_recursive(); // setInterval will never run
    
    const si_recursive = () => setImmediate(si_recursive);
    si_recursive(); // setInterval will run
    
    setInterval(() => console.log('hi'), 10);
    
    function foo(count, callback) {
      if (count <= 0) {
        return process.nextTick(() => callback(new TypeError('count > 0')));
      }
      myAsyncOperation(count, callback);
    }