setImmediate与nextTick

Ben*_*aum 328 javascript node.js setimmediate

Node.js版本0.10今天发布并推出 setImmediate.该API的变化文档建议做递归时使用它nextTick调用.

MDN看起来它看起来非常相似process.nextTick.

nextTick应该何时使用,何时使用setImmediate

Joh*_*yHK 494

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

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

  • 传递给process.nextTick的回调通常会在当前执行流程结束时调用,因此大约与同步调用函数一样快.如果不加以控制,这会使事件循环挨饿,从而阻止任何I/O发生.setImmediates按创建的顺序排队,并在每次循环迭代时从队列中弹出一次.这与process.nextTick不同,它将执行每次迭代的process.maxTickDepth排队回调.在触发排队的回调之后,setImmediate将屈服于事件循环,以确保I/O不会被饿死. (83认同)
  • @fabspro"因为他们不喜欢被我的微软打败"让你听起来很痛苦.这主要是因为它非常可怕,非常有名.如果有一次setImmediate函数将永远不会运行,它会立即执行.函数的名称与它的名称完全相反.切换时,nextTick和setImmediate会更好; setImmediate在当前堆栈完成后立即执行(在等待I/O之前),nextTick在下一个滴答结束时执行(等待I/O之后).但是,这已经说过一千次了. (43认同)
  • @fabspro但不幸的是这个函数叫做nextTick.nextTick"立即"执行,而setImmediate更像是setTimeout/postMessage. (4认同)
  • @UstamanSangat setImmediate仅受IE10 +支持,所有其他浏览器都顽固地拒绝实施可能的未来标准,因为他们不喜欢被微软殴打.要在FF/Chrome中获得类似的结果,您可以使用postMessage(将消息发布到您自己的窗口).您也可以考虑使用requestAnimationFrame,特别是如果您的更新与UI相关.setTimeout(func,0)根本不像process.nextTick那样工作. (2认同)

Dra*_*anS 57

作为例证

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);
  });
});
Run Code Online (Sandbox Code Playgroud)

将给出以下输出

Start
Done
NT1
NT2
NT3
TO1
IO2
IO3
IM1
IM2
IM3
IM4
IO1
Run Code Online (Sandbox Code Playgroud)

我希望这有助于理解其中的差异.

  • 这真的很有帮助谢谢。我认为图像和示例是理解事物的最快方式。 (3认同)
  • 我认为重要的是要指出 setTimeout() 和 setImmediate() 当不在 I/O 周期内时,顺序是不确定的,具体取决于进程的性能。https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/`例如,如果我们运行以下不在 I/O 周期(即主模块)内的脚本,两个计时器的执行顺序是不确定的,因为它受进程性能的约束:`所以,这个答案并没有真正回答确切的区别,而只是一个在不同上下文中可能会有所不同的例子 (3认同)

Che*_*hev 41

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

   ?????????????????????????
??>?        timers         ?
?  ?????????????????????????
?  ?????????????????????????
?  ?     I/O callbacks     ?
?  ?????????????????????????
?  ?????????????????????????
?  ?     idle, prepare     ?
?  ?????????????????????????      ?????????????????
?  ?????????????????????????      ?   incoming:   ?
?  ?         poll          ?<??????  connections, ?
?  ?????????????????????????      ?   data, etc.  ?
?  ?????????????????????????      ?????????????????
?  ?        check          ?
?  ?????????????????????????
?  ?????????????????????????
????    close callbacks    ?
   ?????????????????????????
Run Code Online (Sandbox Code Playgroud)

来源:https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/

请注意,检查阶段紧接在轮询阶段之后.这是因为轮询阶段和I/O回调是您的调用最有可能setImmediate运行的地方.因此理想情况下,大多数这些调用实际上都是非常直接的,只是没有nextTick在每次操作之后检查并且在技术上存在于事件循环之外.

让我们看一下setImmediate和之间区别的一个小例子process.nextTick:

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);
Run Code Online (Sandbox Code Playgroud)

假设我们刚刚运行了这个程序,并且正在逐步完成事件循环的第一次迭代.它将调用step迭代零的函数.然后它将注册两个处理程序,一个用于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
Run Code Online (Sandbox Code Playgroud)

现在让我们将递归调用移动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);
Run Code Online (Sandbox Code Playgroud)

现在我们已经将递归调用移动stepnextTick处理程序中,事物将以不同的顺序运行.我们对事件循环的第一次迭代运行并调用step注册setImmedaite处理程序和nextTick处理程序.当前操作结束后,我们的nextTick处理程序触发,递归调用step并注册另一个setImmediate处理程序以及另一个nextTick处理程序.由于nextTick处理程序在当前操作之后触发,因此nextTicknextTick处理程序中注册处理程序将导致第二个处理程序在当前处理程序操作完成后立即运行.该nextTick处理程序将持续开火,阻止当前事件循环从不断持续.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
Run Code Online (Sandbox Code Playgroud)

请注意,如果我们没有中断递归调用并在10次迭代后中止它,那么nextTick调用将保持递归并且永远不会让事件循环继续到下一阶段.这是如何nextTick在递归使用时变为阻塞,而setImmediate在下一个事件循环中触发并setImmediate从一个内部设置另一个处理程序将根本不中断当前事件循环,允许它继续正常执行事件循环的阶段.

希望有所帮助!

PS - 我同意其他评论者的说法,两个函数的名称可以很容易地交换,因为nextTick它会在下一个事件循环中触发,而不是在当前循环结束时触发,并且当前循环的结束更"立即" "比下一个循环的开始.哦,这就是我们在API成熟时所获得的,人们开始依赖现有的接口.

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

Jay*_*Zee 29

在答案的评论中,它没有明确说明nextTick从Macrosemantics转移到Microsemantics.

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

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

有关工具和详细信息,请查看https://github.com/YuzuJS/setImmediate


Bri*_*ams 10

这里有一些很好的答案,详细说明了它们是如何工作的。

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

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


始终使用setImmediate.


Node.js的事件循环,定时器和process.nextTick()文档包括以下内容:

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


在文档的早些时候,它警告说process.nextTick可能会导致......

一些糟糕的情况,因为它允许您通过递归process.nextTick()调用来“饿死”您的 I/O,从而防止事件循环到达轮询阶段。

事实证明,process.nextTick甚至可以饿死Promises

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');
      })
    })
  })
})
Run Code Online (Sandbox Code Playgroud)

另一方面,setImmediate更容易推理”并避免了这些类型的问题:

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

setImmediate(() => {
  console.log('this happens LAST');
})
Run Code Online (Sandbox Code Playgroud)

因此,除非对 的独特行为有特定需求,否则process.nextTick推荐的方法是“在所有情况下使用setImmediate()”。


Bha*_*Raj 9

简单来说,process.NextTick()将在事件循环的下一个刻度处执行.但是,setImmediate基本上有一个单独的阶段,它确保只在IO回调和轮询阶段之后才会调用在setImmediate()下注册的回调.

请参考此链接以获得更好的解释:https: //medium.com/the-node-js-collection/what-you-should-know-to-really-understand-the-node-js-event-loop-and -它的度量-c4907b19da4c

简化的事件循环事件