在Nodejs事件循环的哪个阶段执行resolved promise的回调?

wmvff8tz  于 2023-04-20  发布在  Node.js
关注(0)|答案(2)|浏览(130)

根据this answer
“重要的是要考虑到promise中的then/catch回调是微任务,将在nextTick任务之后执行”
Nodejs文档中提到nextTickQueue将在当前操作完成后被处理,而不管事件循环的当前阶段。这是否意味着resolved promises回调不会在poll阶段或pending callbacks阶段执行?
此外,this video提到有一个queueMicroTask队列,它在事件循环的当前阶段之后处理。所以我现在理解的是:
已解决的promises回调将被添加到queueMicroTask,该queueMicroTask将在nextTickQueue(传递给process.nextTick的回调)之后处理,该队列将在事件循环的当前阶段之后处理。
是这样吗?如果不是请纠正我,我真的很困惑。

cbjzeqam

cbjzeqam1#

微任务和nextTicks有一个重要的语义,这取决于Node的版本。

**在Node v11之前,**nextTick队列在事件循环的各个阶段之间执行(定时器、I/O、立即数、关闭句柄是四个阶段),因此Node v11之前,promise回调也在事件循环的各个阶段之间执行(我在这里详细写过:https://blog.insiderattack.net/promises-next-ticks-and-immediates-nodejs-event-loop-part-3-9226cbe7a6aa
**然而,从Node v11开始,**事件循环会跳转到microtask队列,每当microtask作为程序执行的一部分添加到微任务队列时。您可以使用以下片段进行实验。同样适用于nextTick队列。您可以在这里阅读更多内容:https://blog.insiderattack.net/new-changes-to-timers-and-microtasks-from-node-v11-0-0-and-above-68d112743eb3

setImmediate(() => console.log('timeout1'));
setImmediate(() => {
    console.log('timeout2')
    Promise.resolve().then(() => console.log('promise'))
});
setImmediate(() => console.log('timeout3'));
setImmediate(() => console.log('timeout4'));

上述代码的输出根据Node.js版本的不同而变化,如下所示:

$ node -v
v10.19.0
$ node test.js    
timeout1
timeout2
timeout3
timeout4
promise

$ nvm use 11
Now using node v11.15.0 (npm v6.7.0)
$ node test.js        
timeout1
timeout2
promise
timeout3
timeout4

因此,重要的是要知道nextTicksmicrotasks在Node版本〉=11中具有更高的优先级,因为它们有机会在事件循环的当前阶段中被处理。但在早期的Node版本中,nextTicksmicrotasks在循环的每个阶段结束时执行。

顺便说一下,重要的是要知道microtasks队列是v8引擎的一部分,并且不在Node.js运行时中维护。但是,一旦Node.js完成nextTick队列,Node.js事件循环指示v8运行所有microtasks。因此,promise回调在nextTick队列之后执行。

cgyqldqp

cgyqldqp2#

Nodejs文档中提到nextTickQueue将在当前操作完成后被处理,而不管事件循环的当前阶段。这是否意味着resolved promises回调不会在poll阶段或pending callbacks阶段执行?
这肯定是一种模棱两可的信息。它们可以在任何一个阶段运行,但它们并不排他于特定的阶段。
已解决的promises回调将被添加到queueMicroTask,该queueMicroTask将在nextTickQueue(传递给process.nextTick的回调)之后处理,该队列将在事件循环的当前阶段之后处理。
不是在每个阶段结束时,它可能发生在任何阶段的任何时候(据我所知)。
假设你有两个计时器已经过期,并且你处于事件循环的计时器过期阶段。这意味着在这个阶段有两个回调要处理。让我们假设第一个计时器在已经解析的promise上调用then。(它可以马上到达它)。如果这是在阶段结束时发射的,那么第二个定时器回调会在之前运行,因为它还没有结束。
这里真正发生的是,第一个计时器回调运行并将回调添加到微任务队列中,该队列在回调完成后和继续当前事件循环阶段之前被消耗。
可以运行的示例:

setTimeout(() => {
    console.log('first');
    let p = Promise.resolve();
    p.then(() => console.log('second'));
    p.then(() => console.log('third'));
}, 1)

setTimeout(() => {
    console.log('fourth')
}, 1);

相关问题