来自返回 Promise 的 I/O 操作的回调是否会进入 Node 中的 I/O 队列或微任务队列?

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

我刚刚学习 Node 中的事件循环如何工作的细节。

我了解到 Promise 队列的优先级高于计时器队列,后者的优先级高于 I/O 队列。

async function asyncFunc() {
  let sum = 0;
  for (let i = 0; i < 100000; i++) {
    sum += i;
  }
}
async function main() {
  let start = Date.now();
  let interval = setInterval(() => {
    let time = Date.now() - start;
    console.log(time);
  }, 1000);
  while (true) {
    await asyncFunc();
  }
}
main();

此代码从不记录时间,因为承诺队列永远不会清除。

import fs from "fs-extra";

async function asyncFunc(j) {
  let sum = 0;
  console.log("checkpoint 1");
  await fs.copy("file.txt", `file${j}.txt`, { overwrite: false });
  for (let i = 0; i < 1000000000; i++) {
    sum += i;
  }
  console.log("checkpoint 2");
}

async function main() {
  let start = Date.now();
  let interval = setInterval(() => {
    let time = Date.now() - start;
    console.log(time);
  }, 1000);

  while (true) {
    const promises = [];
    for (let j = 0; j < 4; j++) {
      promises.push(asyncFunc(j));
    }
    await Promise.all(promises);
  }
}

main();

为什么会输出这段代码

checkpoint 1
checkpoint 1
checkpoint 1
checkpoint 1
checkpoint 2
checkpoint 2
checkpoint 2
2123
checkpoint 2

在 cpu 密集型工作开始之前,所有副本都会在空文件上调用,并且它们应该花费很少的时间。

如果文件复制完成时向 Promise 队列返回回调,我认为永远不应该记录时间,因为 Promise 队列在 cpu 密集的工作完成时始终会有一个回调在等待。

如果它向 I/O 队列返回回调,我认为时间日志将在最后一个检查点 1 之后发生,因为承诺队列将为空并且 I/O 队列将有四个回调。

这是怎么回事?这只是一个奇怪的竞争条件吗?

javascript node.js promise race-condition event-loop
1个回答
0
投票

定时器队列中的作业比 I/O 队列中的作业优先级的原因在文章 Node.js 事件循环 (nodejs.org) 中进行了解释,其中相关的两个阶段被命名为“定时器” ”和“轮询”(针对 I/O):

为了防止轮询阶段使事件循环处于饥饿状态,libuv(实现 Node.js 事件循环和平台所有异步行为的 C 库)在停止轮询更多内容之前也有一个硬性最大值(取决于系统)事件。

再次:

如果轮询队列不为空,事件循环将迭代其回调队列,同步执行它们,直到队列耗尽或达到系统相关的硬限制。

这显然是您的场景中发生的情况:执行了三个繁忙循环后,事件循环认为轮询阶段已经足够长了,并进入循环的下一个阶段,暂时放弃非空 I /O 队列。

因此计时器回调有机会执行,之后循环返回轮询阶段以继续处理该队列中的剩余作业。

© www.soinside.com 2019 - 2024. All rights reserved.