process.nextTick 与微任务执行顺序

问题描述 投票:0回答:1

我在节点 js 中有以下代码片段


Promise.resolve().then(() => {
  Promise.resolve().then(() => console.log('promise'));
  process.nextTick(() => console.log('nextTick'));
});

// promise
// nextTick

我有这个片段

Promise.resolve().then(() => console.log('promise'));
process.nextTick(() => console.log('nextTick'));

// nextTick
// promise

从 nodejs 的文档中,我读到传递给 process.nextTick() 的所有回调将在事件循环继续之前得到解决

我很清楚,在第二个示例中,next tick 首先执行,因为它在事件循环的每次迭代之前执行。 但是为什么在第一个例子中它在 promise.resolve() 之后执行?

javascript node.js event-loop
1个回答
2
投票

前言: 这只是我们可以推理的事情,因为我们知道当履行处理程序通过then附加到它时,内部承诺

已经
履行。但一般来说,在您的代码观察到它之前,请避免假设您知道承诺的状态。如果在事件发生之前内部承诺没有实现,则
nextTick
处理程序将首先被调用。


要理解为什么在

nextTick
回调之前调用第二个履行处理程序,让我们看看 event loop guide 关于
nextTick
的内容:

您可能已经注意到

process.nextTick()
没有显示在图中,即使它是异步 API 的一部分。这是因为
process.nextTick()
在技术上不是事件循环的一部分。相反,
nextTickQueue
将在当前操作完成后处理
,而不管事件循环的当前阶段。在这里,一个操作被定义为从底层 C/C++ 处理程序的转换,并处理需要执行的 JavaScript。

(我的重点)

让我们看看上面的代码做了什么:

  • 主脚本通过
    Promise.resolve().then
    在微任务中安排一个 promise fulfillment 回调,它是这样做的:
    • 在微任务中安排另一个承诺履行回调
    • 安排
      nextTick
      回调

我不能说我已经深入研究了相关的 Node.js 代码,但看起来没有涉及“操作转换”。微任务队列在每个 JavaScript 任务结束时被处理直到它为空。¹在这种情况下,任务是主脚本执行;清空微任务队列的循环在该任务的末尾。它选取主脚本排队的微任务,并在运行该微任务的过程中将另一个微任务排队(履行处理程序记录

"promise"
)。由于循环一直运行到微任务队列为空,因此它会在转换回 C/C++ 处理程序之前执行第二个微任务。

同样,我们只能以这种方式对此进行推理,因为我们知道(通过查看代码)当处理程序附加到它时内部承诺的状态是什么,而我们通常不会知道。 :-)


¹ 我应该注意,据我所知,这不是 Node.js 的特定行为(它在网络平台上感谢 kaiido ),但考虑到 Node.js 项目团队正在尝试尽可能与 Web 平台保持一致,Node.js 不太可能在这方面以自己的方式发展。但目前,不幸的是,上面的 Node.js 事件循环文档没有谈论微任务。

只是为了演示一个极端的例子,这里是一些非常糟糕的代码的例子,通过重复排队微任务使事件循环挨饿 20 秒,阻止

nextTick
处理程序在微任务之前运行,直到微任务停止排队新的微任务:

// Starving the event loop for 20 seconds by scheduling microtasks
// Obviously, don't do this! :-)
console.log(new Date().toISOString());
let count = 0;
const stop = Date.now() + 20000;
const handler = () => {
    if (Date.now() < stop) {
        ++count;
        Promise.resolve().then(handler);
    }
};
// Inside a microtask...
Promise.resolve().then(() => {
    // ...schedule next tick callback...
    process.nextTick(() => {
        console.log(new Date().toISOString());
        console.log(count);
    });
    // ...and queue nested microtasks until 20 seconds have passed
    handler();
});
© www.soinside.com 2019 - 2024. All rights reserved.