美文网首页
【二】Node.js事件循环中nextTick和Promise队

【二】Node.js事件循环中nextTick和Promise队

作者: 涅槃快乐是金 | 来源:发表于2024-03-05 20:52 被阅读0次

欢迎来到系列文章中的第二篇,我们将继续以可视化方式探究 Node.js 事件循环。在第一篇文章中,我们了解到事件循环是 Node.js中至关重要的一部分,帮助协调同步和异步代码的执行。

它由六个不同的队列组成。一个 nextTick 队列和一个 promise 队列(在本系列文章中称为微任务队列),一个定时器队列,一个 I/O 队列,一个检查队列,最后一个是关闭队列。

在每个循环中,当适当时,回调函数会从队列中出列并在调用堆栈上执行。在开始这篇文章之前,让我们先了解如何在这些队列中排队回调函数。

回调函数排队

要在 nextTick 队列中排队回调函数,我们使用内置的 process.nextTick() 方法。语法很简单:process.nextTick(callbackFn)。当此方法在调用堆栈上执行时,回调函数将被排队到 nextTick队列中。

要在 promise 队列中排队回调函数,我们将使用 Promise.resolve().then(callbackFn)。当 promise解决时,传递给 then() 块的函数将被排队到 promise 队列中。

现在我们知道如何向这两个队列中添加回调函数了,让我们从第一个实验开始。

所有实验都使用CommonJS 模块格式进行。

实验 1
// index.js
console.log("console.log 1");
process.nextTick(() => console.log("this is process.nextTick 1"));
console.log("console.log 2");

这里有一段最小的代码片段,记录了三个不同的语句。第二个语句使用 process.nextTick() 方法将回调函数排队到 nextTick 队列中。

第一个 console.log()语句被推入调用堆栈并执行。它将相应的消息记录到控制台,然后从堆栈中弹出。

接下来,·process.nextTick()· 在调用堆栈上执行。这将回调函数排队到 nextTick 队列中,并被弹出。由于仍然有用户编写的代码要执行,回调函数必须等待执行。

执行继续,并且最后一个 console.log()语句被推入堆栈。消息被记录到控制台,并将函数从堆栈中弹出。现在,没有更多用户编写的同步代码要执行了,因此控制权进入事件循环。

来自nextTick队列的回调函数被推到堆栈上,console.log()也被推到堆栈上,执行并将相应的消息记录到控制台。

猜测
所有用户编写的同步 JavaScript代码优先于运行时希望最终执行的异步代码。

让我们继续进行第二个实验。

实验 2
// index.js
Promise.resolve().then(() => console.log("this is Promise.resolve 1"));
process.nextTick(() => console.log("this is process.nextTick 1"));

我们调用了一次 Promise.resolve().then()和一次process.nextTick()

当调用堆栈执行第 1 行时,它将回调函数排入 promise 队列中。

当调用堆栈执行第 2 行时,它将回调函数排入 nextTick 队列中。

在第 2 行之后,没有更多的用户编写的代码要执行。

控制权进入事件循环,其中nextTick 队列优先于 promise队列(这是 Node.js运行时的工作原理)。

事件循环执行 nextTick 队列回调函数,然后执行 promise 队列回调函数。

控制台显示 "this is process.nextTick 1",然后显示"this is Promise.resolve 1"

猜测
在 nextTick 队列中的所有回调在 promise 队列中的所有回调之前执行。

让我为您详细解释上述第二个实验的更复杂版本。

额外实验
// index.js
process.nextTick(() => console.log("this is process.nextTick 1"));
process.nextTick(() => {
  console.log("this is process.nextTick 2");
  process.nextTick(() =>
    console.log("this is the inner next tick inside next tick")
  );
});
process.nextTick(() => console.log("this is process.nextTick 3"));

Promise.resolve().then(() => console.log("this is Promise.resolve 1"));
Promise.resolve().then(() => {
  console.log("this is Promise.resolve 2");
  process.nextTick(() =>
    console.log("this is the inner next tick inside Promise then block")
  );
});
Promise.resolve().then(() => console.log("this is Promise.resolve 3"));

代码包含三个对 process.nextTick() 的调用和三个对 Promise.resolve()语句的调用。每个回调函数都记录了相应的消息。

当调用堆栈执行第1行时,它将回调函数排入 Promise 队列。

当调用堆栈执行第2行时,它将回调函数排入 nextTick 队列。

在第2行之后没有更多的用户编写的代码需要执行。

控制权进入事件循环,在此处 nextTick 队列优先于 promise 队列(这是 Node.js 运行时的工作原理)。

事件循环执行 nextTick 队列的回调函数,然后执行 promise 队列的回调函数。

控制台显示 "this is process.nextTick 1",然后是 "this is Promise.resolve 1"。

猜测
所有 nextTick 队列中的回调函数在 promise 队列中的回调函数之前执行。

让我带您更详细地了解上述第二个实验的更复杂版本。

额外实验2
// index.js
process.nextTick(() => console.log("this is process.nextTick 1"));
process.nextTick(() => {
  console.log("this is process.nextTick 2");
  process.nextTick(() =>
    console.log("this is the inner next tick inside next tick")
  );
});
process.nextTick(() => console.log("this is process.nextTick 3"));

Promise.resolve().then(() => console.log("this is Promise.resolve 1"));
Promise.resolve().then(() => {
  console.log("this is Promise.resolve 2");
  process.nextTick(() =>
    console.log("this is the inner next tick inside Promise then block")
  );
});
Promise.resolve().then(() => console.log("this is Promise.resolve 3"));

该代码包含三个对 process.nextTick() 的调用和三个对 Promise.resolve()语句的调用。每个回调函数记录了相应的消息。

然而,第二个 process.nextTick()和第二个Promise.resolve()都有一个额外的process.nextTick()语句,每个都带有一个回调函数。

为了加快对此流程的解释,我将省略调用堆栈。当调用堆栈执行了所有六个语句时,nextTick 队列中有三个回调函数,而 promise 队列中有三个。在没有其他要执行的内容时,控制权进入事件循环。

如我们所知,nextTick队列具有优先级。首先执行第一个回调函数,并将相应的消息记录到控制台。

接下来,执行第二个回调函数,该函数记录第二个日志语句。然而,此回调函数包含另一个 process.nextTick()的调用,该调用将内部日志语句排入 nextTick队列的末尾。

然后,Node 执行第三个 nextTick 回调,并将相应的消息记录到控制台。最初只有三个回调函数,但第二个回调函数添加了另一个回调函数到队列中,现在轮到了它。

事件循环推送内部 nextTick 回调,并执行 console.log()语句。

nextTick队列为空,控制流转到promise队列。promise 队列类似于 nextTick 队列。

首先记录 "Promise.resolve 1",然后是 "Promise.resolve 2"。但是,使用 process.nextTick()将一个函数添加到 nextTick 队列中。尽管如此,控制仍然留在promise 队列中,并继续执行其他回调函数。然后我们得到 Promise.resolve 3,此时 promise 队列为空。

Node将再次检查微任务队列中是否有新的回调。由于 nextTick队列中有一个回调,它会执行该回调,导致我们的最后一条日志语句。

这可能是一个稍微高级的实验,但推断仍然是相同的。

猜测
在所有回调函数执行之前,nextTick 队列中的所有回调函数都会执行。

在使用 process.nextTick()时要小心。过度使用此方法可能会导致事件循环饥饿,从而阻止队列的其余部分运行。即使有大量的 nextTick()调用,也可能会阻止I/O队列执行自己的回调。官方文档建议使用 process.nextTick()有两个主要原因:处理错误或在调用堆栈展开之后但在事件循环继续之前运行回调。在使用 process.nextTick()时,请务必谨慎使用。

额外实践

实验表明,所有用户编写的同步 JavaScript代码优先于运行时希望最终执行的异步代码,并且在 nextTick 队列中的所有回调函数之前,会执行所有promise 队列中的回调函数。

相关文章

  • node中事件循环

    一、优先级最高的为nextTick和Promise 二、事件循环中,必须先清空nextTick和Promise 1...

  • node.js延时 时序问题探索

    node.js实现延时的方法有: process.nextTick(() => { }); Promise.res...

  • 深入浅出nodejs

    setImmediate()和process.nextTick()区别,前者是每个回调将再一次事件循环中执行,后者...

  • 事件循环

    Node事件环 微任务的概念(promise.then process.nextTick) timers 时间 s...

  • 程序员的日常记录

    ?事件循环Node.js Event Loop 的理解 Timers,process.nextTick()Node...

  • nextTick的原理

    什么是nextTick呢?nextTick,我可以理解为next是下一个的意思,在事件循环中,每进行一次循环操作称...

  • Nodejs事件轮循

    事件轮循分为本轮和次轮 本轮的执行顺序为: 同步代码 process.nextTick 微任务 次轮的顺序为: t...

  • 异步任务中在分为宏任务和微任务

    常见的微任务有:process.nextTick、Promise和 MutationObserver(监听DOM变...

  • event loop执行顺序汇总

    包含process.nextTick, promise, settimeout, setimmediate 一,先...

  • node 事件

    1、事件 1.1普通事件的使用 1.2、Node.js 的事件循环机制解析 1)Node 由事件循环开始,到事件循...

网友评论

      本文标题:【二】Node.js事件循环中nextTick和Promise队

      本文链接:https://www.haomeiwen.com/subject/fhbdzdtx.html