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

从MDN看来,它与process.nextTick非常相似。

我何时应该使用nextTick以及何时应该使用setImmediate

评论

博客blog.nodejs.org/2013/03/11/node-v0-10-0-stable
上有5个关于此更改的段落
从性能基准来看,在大型计算中,nextTick似乎比setImmediate快。

作为记录,我首先阅读了这五个段落,但是当它并没有真正为我解决任何问题时,仍然结束了这个问题。接受的答案更加简洁,实际上更详细地描述了setImmediate的功能。

我已经在博客中详细说明了差异。

GC是否可以在setImmediate之前运行但不能在nextTick之前运行?

#1 楼

如果要将函数放在事件队列中已经存在的任何I / O事件回调后面,请使用setImmediate。使用process.nextTick将函数有效地排队在事件队列的开头,以便在当前函数完成后立即执行。

因此,在尝试中断长时间运行的情况下,CPU使用递归的绑定作业,您现在想使用setImmediate而不是process.nextTick来排队下一次迭代,因为否则任何I / O事件回调都将没有机会在迭代之间运行。

评论


传递给process.nextTick的回调通常将在当前执行流的结尾被调用,因此,其调用速度与同步调用一个函数差不多。如果不进行检查,这将使事件循环陷入饥饿状态,从而防止发生任何I / O。 setImmediates按创建的顺序排队,并在每次循环迭代时从队列中弹出一次。这与process.nextTick不同,后者将在每次迭代中执行process.maxTickDepth排队的回调。在触发排队的回调以确保I / O不会饿死之后,setImmediate将屈服于事件循环。

–本杰明·格伦鲍姆(Benjamin Gruenbaum)
13年4月23日在0:48

@UstamanSangat setImmediate仅受IE10 +支持,所有其他浏览器都顽固地拒绝实施可能的未来标准,因为它们不喜欢被Microsoft打败。要在FF / Chrome中获得相似的结果,可以使用postMessage(将消息发布到自己的窗口中)。您也可以考虑使用requestAnimationFrame,特别是如果您的更新与UI相关。 setTimeout(func,0)根本不像process.nextTick。

– fabspro
13年8月3日在6:11

@fabspro“因为他们不喜欢被我的微软击败”,这让您听起来有些痛心。这主要是因为它的名字非常可怕。如果有一次setImmediate函数永远不会运行,那么它将立即生效。函数的名称与函数的名称完全相反。 nextTick和setImmediate最好切换一下; setImmediate在当前堆栈完成后立即执行(在等待I / O之前),nextTick在下一个滴答结束时执行(在等待I / O之后)。但是,这已经被说了一千遍了。

– Craig Andrews
13年9月29日在21:31

@fabspro但不幸的是,该函数称为nextTick。当setImmediate更像是setTimeout / postMessage时,nextTick将立即执行。

–罗伯特
13-10-10在2:05

@CraigAndrews我会避免requestAnimationFrame,因为它并不总是发生(我肯定已经看到了,我认为示例不是tab就是当前的tab),并且可以在页面完成绘制之前调用它(即浏览器仍在忙于绘制) 。

–robocat
2014年3月19日下午1:00

#2 楼

作为说明
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事件之后。
Mario Casciaro撰写的Node.js设计模式(可能是有关node.js / js的最佳书)


评论


这真的很有帮助,谢谢。我认为图像和示例是理解某些事物的最快方法。

–约翰·詹姆斯(John James)
18 Mar 4 '18 at 20:25

我认为必须指出,setTimeout()和setImmediate()不在I / O周期内时,其顺序是不确定的,具体取决于进程的性能。例如,如果我们运行以下不在I / O周期(即主模块)内的脚本,则这两个脚本的顺序计时器的执行是不确定的,因为它受流程性能的约束:因此,此答案并不能真正回答确切的差异,而只是一个示例,该示例可能会在不同的上下文中发生变化

– Actung
18-10-30在13:03



正如@Actung指出的那样。知道setTimetout和setImmediate是否在I / O周期内对于确定结果非常重要。

–拉吉卡·伊马尔(Rajika Imal)
18/12/24在16:54



#3 楼

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

docs / guides / event-loop-timers-and-nexttick /

请注意,检查阶段紧接在轮询阶段之后。这是因为轮询阶段和I / O回调最有可能在您的setImmediate调用上运行。因此,理想情况下,这些调用中的大多数实际上实际上是立即调用的,而不像nextTick那样立即调用,因为setImmediate在每次操作后都会被检查并且从技术上讲是存在于事件循环之外的。 process.nextTickstep之间的区别:

   ┌───────────────────────┐
┌─>│        timers         │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     I/O callbacks     │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     idle, prepare     │
│  └──────────┬────────────┘      ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │
│  │         poll          │<─────┤  connections, │
│  └──────────┬────────────┘      │   data, etc.  │
│  ┌──────────┴────────────┐      └───────────────┘
│  │        check          │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
└──┤    close callbacks    │
   └───────────────────────┘


假设我们只运行了该程序,并逐步完成了事件循环的第一次迭代。它将以零迭代次数调用setImmediate函数。然后它将注册两个处理程序,一个用于process.nextTick,另一个用于setImmediate。然后,我们从nextTick处理程序中递归调用此函数,该处理程序将在下一个检查阶段运行。 nextTick处理程序将在当前操作结束时运行,以中断事件循环,因此即使它已被第二次注册,它也将首先运行。 ,下一个事件循环开始,执行正常的事件循环阶段,触发setImmediate并递归调用我们的step函数,以重新开始该过程。当前操作结束,nextTick触发等。

上面代码的输出将是:将step替换为我们的nextTick处理程序,而不是setImmediate

function step(iteration) {
  if (iteration === 10) return;
  setImmediate(() => {
    console.log(`setImmediate iteration: ${iteration}`);
    step(iteration + 1); // Recursive call from setImmediate handler.
  });
  process.nextTick(() => {
    console.log(`nextTick iteration: ${iteration}`);
  });
}
step(0);


现在,我们已经将对step的递归调用移动到nextTick处理程序中,事情将以不同的顺序表现。事件循环的第一个迭代运行并调用step,注册一个setImmedaite处理程序以及一个nextTick处理程序。当前操作结束后,我们的nextTick处理程序将触发,递归调用step并注册另一个setImmediate处理程序以及另一个nextTick处理程序。由于nextTick处理程序在当前操作后触发,因此在nextTick处理程序中注册nextTick处理程序将导致第二个处理程序在当前处理程序操作完成后立即运行。 nextTick处理程序将继续触发,以防止当前事件循环继续进行。在看到单个nextTick处理程序火灾之前,我们将遍历所有setImmediate处理程序。 />请注意,如果我们没有中断递归调用并在10次迭代后终止它,则nextTick调用将继续递归,并且永远不会让事件循环继续进行到下一个阶段。这就是nextTick在递归使用时会变成阻塞的方式,而setImmediate将在下一个事件循环中触发,并且从一个内部设置另一个setImmediate处理程序根本不会中断当前事件循环,从而使其能够照常继续执行事件循环的各个阶段

希望能有所帮助!事件循环而不是当前循环的结束,并且当前循环的结束比下一个循环的开始更“直接”。哦,好了,这就是我们随着API的成熟和人们开始依赖现有接口而得到的。

评论


很清楚的描述。我认为这个答案需要更多的支持。

– Actung
18-10-30在13:06

讲得好(是)

– Dhiraj Sharma
19年6月26日在3:31

重申Node关于使用process.nextTick的警告很重要。如果在nextTickQueue中加入大量回调,则可以通过确保从未达到轮询阶段来使事件循环饿死。这就是为什么您通常应该首选setImmediate的原因。

– Faridcs
19年8月13日在9:14



谢谢,这是最好的解释。示例代码确实有帮助。

–aman-nidhi
20-2-27在11:09

@skyhavoc很高兴能为您提供帮助!

–雪佛兰
20-2-28在22:47

#4 楼

在答案的评论中,它没有明确指出nextTick从Macrosemantics转变为Microsemantics。

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

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

查看https://github.com/YuzuJS/setImmediate了解工具和详细信息

#5 楼

简单来说,process.NextTick()将在事件循环的下一个计时执行。但是,setImmediate基本上有一个单独的阶段,可以确保在IO回调和轮询阶段之后才调用setImmediate()下注册的回调。 br />https://medium.com/the-node-js-collection/what-you-should-know-to-really-understand-the-node-js-event-loop-and-its-metrics-c4907b19da4c



#6 楼

这里有一些很好的答案,详细介绍了它们的工作原理。

只需添加一个回答所问特定问题的答案即可:


我什么时候应该使用nextTick,什么时候应该使用setImmediate



始终使用setImmediate


我们建议开发人员在所有情况下都使用process.nextTick(),因为这样做更容易推理(并且导致代码与更广泛的环境兼容,例如浏览器JS。)



文档中的早期版本警告说setImmediate()可能导致...


某些不良情况,因为它使您“饥饿”通过进行递归process.nextTick调用来进行I / O,这可以防止事件循环到达轮询阶段。事实证明,process.nextTick()甚至可以饿死process.nextTick

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


另一方面,Promises则“易于推理”,避免了这些问题类型:

Promise.resolve().then(() => { console.log('this happens FIRST'); });

setImmediate(() => {
  console.log('this happens LAST');
})



因此,除非特别需要setImmediate的独特行为,否则建议的方法是“在所有情况下均使用process.nextTick “。

#7 楼

我建议您查看专用于Loop的文档部分,以更好地理解。从此处摘录的一些片段:

就用户而言,我们有两个调用类似,但它们的名称令人困惑。


process.nextTick( )在同一阶段立即触发。
setImmediate()在
事件循环的以下迭代或“滴答”中触发。实质上,应交换名称。 process.nextTick()比setImmediate()触发得更快,但这是过去的产物,不太可能改变。