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

tus*_*har 2 javascript event-loop node.js

我已阅读官方文档和许多其他博客,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);
Run Code Online (Sandbox Code Playgroud)

输出将是:

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

在此期间, 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
Run Code Online (Sandbox Code Playgroud)

但输出结果是:

called inside main fn in first-iteration
called in first iteration
inside fs
process.nextTick
immediate inside fs
timeout inside fs
Run Code Online (Sandbox Code Playgroud)

某处写道,微任务(process.nextTick)以最高优先级执行,因此事件循环将放弃所有执行并乍一看执行微任务。但既然如此,inside fs之前又何必安慰呢process.nextTick

process.nextTick()基本上,我很困惑,事件循环的各个阶段是如何进行的?

San*_*tel 6

节点开发人员最大的误解之一是 process.nextTick 在接下来的刻度(迭代)中运行代码

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

在此输入图像描述

process.nextTick()从技术上讲,它不是事件循环的一部分。相反,nextTickQueue 将会在当前操作完成后进行处理,而不管事件循环的当前阶段如何。Next tik 队列与其他四个主队列分开显示,因为它不是由 libuv 原生提供的,而是在 Node 中实现的

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

  1. 自 Node v11 以来,Node v11 中引入了一些更改,显着更改了 nextTick、Promise 回调、setImmediate 和 setTimeout 回调的执行顺序。

  2. nextTikcs 和 Promise 回调队列在每个计时器和立即回调之间进行处理,无论相应队列中是否已存在其他超时回调和立即回调。

  3. 在nodejs11之前和nodejs11及以上版本运行此https://repl.it/@sandeepp2016/processNextTick#index.js,您会看到执行顺序的差异。

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

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

对于你的最后一个问题,当程序进入main函数时,前三个调用是异步的,前两个调用将排队到事件循环中。但console.log("inside fs")会立即放入调用堆栈并由 Nodejs 主线程执行。process.next也将异步执行(不是 libuv 事件循环的一部分),但在轮询阶段之后和检查阶段之前,如图所示。因此它将inside fs在之前打印process.nextTick

请记住,事件循环仅处理异步代码,同步代码直接在主线程中执行,无需事件循环。

欲了解更多详细信息,请阅读此内容

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/