NodeJS 事件循环中的 process.nextTick() 什么时候被调用?

When is process.nextTick() in the NodeJS event loop called?

我已经阅读了官方文档和 process.nextTick() 上的许多其他博客,但我对某些主题有点困惑。

首先写的是process.nextTick()中的回调在下一次迭代开始前执行。

其次,在 I/O 循环内,事件循环当前处于轮询阶段。在轮询阶段之后,事件循环进入检查阶段,在该阶段执行 setImmediate 方法中调用的任何回调。

现在在下面的代码中

const fs = require('fs');

function main() {
   fs.readFile('./xy.txt', function(err, buff){
      setTimeout(()=> console.log("timeout inside fs"),0);
      setImmediate(()=> console.log("immediate inside fs"));
      process.nextTick(()=>{console.log("process.nextTick")});
      console.log("inside fs");
   })
   console.log("called inside main fn in first-iteration");
}

main();
console.log("called in first-iteration);

输出将是:

called inside main fn in first-iteration
called in first iteration

在此期间,fs fn 的回调将被注册,并且上述两行将被安慰,因为入口脚本将是 运行 没有事件循环。现在,事件循环的第一次迭代将从定时器阶段开始,因为没有定时器,它将进入挂起阶段,然后进入轮询阶段,其中有一个回调(fs fn 回调),事件循环开始执行 fs 回调,现在 setTimeout 回调和 setImmediate 回调将被注册。之后,process.nextTick 回调将被注册到 nextTickQueue 中,它将在下一次迭代或 tick(第二次迭代)中执行。现在轮询阶段结束后,我们在第三行记录了 inside fs

接下来事件循环应该进入检查阶段,其中 setImmediate 回调存在并且应该记录 immediate inside fs 随后事件循环应该进入下一阶段和第一个阶段迭代应该结束。在开始第二次迭代之前,应该记录 process.nextTick,然后最后记录 timeout inside fs

因此最终输出为:

called inside main fn in first-iteration
called in first iteration
inside fs
immediate inside fs
process.nextTick
timeout inside fs

但是输出结果是:

called inside main fn in first-iteration
called in first iteration
inside fs
process.nextTick
immediate inside fs
timeout inside fs

某处写到微任务(process.nextTick)是在最高优先级上执行的,因此事件循环将停止所有执行并执行第一眼看到的微任务。但如果也是这样,为什么 inside fsprocess.nextTick 之前得到安慰?

基本上,我很困惑,process.nextTick() 和事件循环流程是如何进行的?

One of the big misconceptions among node developers is that process.nextTick run code in the following tick(iteration)

在回答你的问题之前,让我告诉你几件事:

process.nextTick() 在技术上不是事件循环的一部分。相反,nextTickQueue 将在当前操作完成后处理,而不管事件的当前阶段 loop.Next tick 队列与其他四个主要队列分开显示,因为它不是 libuv 原生提供的,而是实现的在节点

在事件循环的每个阶段(计时器队列、IO 事件队列、立即队列、关闭处理程序队列是四个主要阶段)之前,在进入该阶段之前,Node 检查 nextTick 队列中是否有任何排队的事件。如果队列不为空,Node 将立即开始处理队列,直到队列为空,然后再进入下一阶段的事件循环。

  1. There are some changes introduced in Node v11 which significantly changes the execution order of nextTick, Promise callbacks, setImmediate and setTimeout callbacks since Node v11.

  2. nextTikcs and promise callback queues are processed between each timer and immediate callback no matter if othere timeouts callback and immidate callback already present in corresponding queue.

  3. Run this https://repl.it/@sandeepp2016/processNextTick#index.js in before nodejs11 and nodejs11 and above you would see the difference in the execution order.

除了 nextTick 被调用以及同步和非同步代码在 Nodejs 中如何执行之外,您几乎正确地理解了流程。

当事件循环进入轮询阶段时,它将执行 fs.read 排队到 io 事件队列的回调。由于队列中没有更多的 io 事件回调,事件循环将进入检查阶段,但在检查阶段的 运行 回调之前,它将执行所有 nexttikcs 和承诺任务队列。这就是为什么 process.nexttick 在你的案例中被打印然后 immediate inside fs 在检查阶段之后它将回到计时器阶段并打印 timeout inside fs.

最后一个问题,当程序进入main函数时,前三个调用是异步的,前两个会排队到事件循环中。但是 console.log("inside fs") 将立即被放入调用堆栈并由 Nodejs main tread 执行。 process.next 也将异步执行(不是 libuv 事件循环的一部分)但在 image.Hence 中描述的轮询阶段之后和检查阶段之前它将在 [=21] 之前打印 inside fs =].

记住事件循环只处理异步代码,同步代码直接在主线程中执行,没有事件循环。

有关详细信息,请阅读此内容

https://blog.insiderattack.net/promises-next-ticks-and-immediates-nodejs-event-loop-part-3-9226cbe7a6aa

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