NodeJS process.nextTick与commonJs和ESM中的queueMicrotask,执行命令是什么?

njthzxwz  于 2023-06-05  发布在  Node.js
关注(0)|答案(1)|浏览(227)

假设我们有一个包含Next.js代码的文件:

process.nextTick(()=>{
    console.log('nextTick')
})

queueMicrotask(()=>{
    console.log('queueMicrotask')
})

console.log('console.log')

我们在package.json中将module system设置为"type": "commonjs"
我们期望控制台中的输出是什么?Node.js官方文档说道:

  • 在Node.js事件循环的每一轮中,process.nextTick()队列总是在微任务队列之前处理 *

因此,在控制台中,我们期望

console.log
nextTick
queueMicrotask

这很有效。直到我将模块系统更改为"type": "module"。在此更改之后,我经常在process.nextTick之前执行queueMicrotask。控制台输出为:

console.log
queueMicrotask
nextTick

有人能解释这种行为吗?我假设这种行为在某种程度上与模块评估和执行过程有关,并且nextTick queueMicrotask在某种程度上进入了不同的事件循环,因为模块意味着异步执行(在浏览器中)。尽管如此,这种猜测是非常不可靠的,从我的Angular 来看,是不合逻辑的。尽管如此,我还是没有一个合理的解释。

rkkpypqq

rkkpypqq1#

这是因为,当作为ESM运行时,脚本实际上已经处于微任务阶段。因此,从那里排队的新微任务将在“nextTick”回调之前执行。
两个队列在这里的行为是相同的,因为它们在完成之前都是“实时”清空的,因此任何新的队列回调都将在访问任何其他队列之前执行。

queueMicrotask(() => {
  let winner;
  console.log("in microtask, the winner is");
  queueMicrotask(() => {
    if (!winner) console.log(winner = "microtask");
  });
  process.nextTick(() => {
    if (!winner) console.log(winner = "nextTick");
  });
});
process.nextTick(() => {
  let winner;
  console.log("in nextTick, the winner is");
  queueMicrotask(() => {
    if (!winner) console.log(winner = "microtask");
  });
  process.nextTick(() => {
    if (!winner) console.log(winner = "nextTick");
  });
});

无论主脚本如何计算,上面的脚本将始终输出“In microtask the赢家is microtask”和“In nextTick the winner is nextTick”。(但两个语句的顺序会发生变化)。
在node中,ESM模块脚本实际上是从 async/await 函数中执行的:

async run() {
  await this.instantiate();
  const timeout = -1;
  const breakOnSigint = false;
  try {
    await this.module.evaluate(timeout, breakOnSigint);

所以这意味着当我们的脚本运行时,我们已经在微任务阶段,因此从那里排队的新微任务将首先执行。
当作为CommonJS运行时,我们仍然处于轮询阶段。入口点在这里,然后所有的事情直到实际的评估是同步的。从轮询阶段开始,nextTick将战胜微任务阶段:

setImmediate(() => {
  let winner;
  console.log("in poll phase, the winner is");
  queueMicrotask(() => {
    if (!winner) console.log(winner = "microtask");
  });
  process.nextTick(() => {
    if (!winner) console.log(winner = "nextTick");
  });
});

这将始终输出“in poll phase the赢家is nextTick”,无论主脚本如何被评估。

相关问题