Art*_*nko 9 javascript event-loop commonjs node.js es6-modules
假设我们有一个包含以下 JS 代码的文件:
process.nextTick(()=>{
console.log('nextTick')
})
queueMicrotask(()=>{
console.log('queueMicrotask')
})
console.log('console.log')
Run Code Online (Sandbox Code Playgroud)
我们已经"type": "commonjs"在 package.json 中设置了模块系统
我们期望控制台输出什么?Node.js 官方文档说:
在 Node.js 事件循环的每一轮中,process.nextTick() 队列始终在微任务队列之前处理
所以,在控制台中我们期望
console.log
nextTick
queueMicrotask
Run Code Online (Sandbox Code Playgroud)
那行得通。直到我将模块系统更改为"type": "module". 在那次更改之后,我不断地在 process.nextTick 之前执行queueMicrotask。控制台输出为:
console.log
queueMicrotask
nextTick
Run Code Online (Sandbox Code Playgroud)
有人能解释这种行为吗?我假设这种行为在某种程度上与模块评估和执行过程有关,并且 nextTick queueMicrotask 以某种方式进入不同的事件循环滴答,因为模块意味着异步执行(在浏览器中)。尽管如此,这种猜测还是非常不稳定,从我的角度来看,这是不合逻辑的。尽管如此,我仍然没有一个合理的解释。
这是因为,当作为 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");
});
});
Run Code Online (Sandbox Code Playgroud)
无论主脚本如何评估,上面的脚本将始终输出“ In microtask the winner is microtask ”和“ In nextTick the winner is nextTick ”。(但这两个语句的顺序将会改变)。
在 Node 中,ESM 模块脚本实际上是通过async/await函数进行评估的:
Run Code Online (Sandbox Code Playgroud)async run() { await this.instantiate(); const timeout = -1; const breakOnSigint = false; try { await this.module.evaluate(timeout, breakOnSigint);
因此,这意味着当我们的脚本运行时,我们已经进入微任务阶段,因此从那里排队的新微任务将首先执行。
但当作为 CommonJS 运行时,我们仍处于轮询阶段。入口点就在这里,然后一切直到实际评估都是同步的。而从 poll 阶段来看,nextTick将会战胜 microtask 阶段:
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");
});
});
Run Code Online (Sandbox Code Playgroud)
无论主脚本如何评估,这将始终输出“在轮询阶段获胜者是 nextTick ”。