【问题标题】:When is process.nextTick() in the NodeJS event loop called?NodeJS 事件循环中的 process.nextTick() 何时被调用?
【发布时间】:2020-10-08 14:23:59
【问题描述】:

我已经阅读了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 中,该回调将在下一次迭代或滴答(第二次迭代)中执行。现在轮询阶段结束后,我们在第三行记录了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 fs 会在process.nextTick 之前得到安慰?

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

【问题讨论】:

    标签: javascript node.js event-loop


    【解决方案1】:

    节点开发人员的一大误解是 process.nextTick 在下面的tick(iteration)中运行代码

    在回答你的问题之前,让我告诉你一些事情:

    process.nextTick() 在技术上不是事件循环的一部分。取而代之的是,nextTickQueue 会在当前操作完成后处理,而不管事件循环的当前阶段。next tick queue 与其他四个主队列分开显示,因为它不是 libuv 原生提供的,而是在 Node 中实现的

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

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

    2. 无论其他超时回调和 immidate 回调已经存在于相应的队列中。

    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 事件循环的一部分),但在轮询阶段之后和检查阶段之前,如图所示。因此它将在process.nextTick 之前打印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/

    【讨论】:

    • 非常感谢,您确实帮了大忙。我会浏览博客并询问任何进一步的问题。
    • 我想和你联系,请检查你的链接
    • 我很高兴它有帮助。
    猜你喜欢
    • 2018-10-16
    • 2012-05-27
    • 1970-01-01
    • 2012-05-09
    • 2015-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多