gpt4 book ai didi

javascript - 意外行为将 process.nextTick 与 async/await 混合。事件循环在这里如何工作?

转载 作者:行者123 更新时间:2023-12-03 00:48:59 32 4
gpt4 key购买 nike

我的代码:

async function run(){

process.nextTick(()=>{
console.log(1);
});

await (new Promise(resolve=>resolve()).then(()=>{console.log(2)}));

console.log(3);

process.nextTick(()=>{
console.log(4);
});

new Promise(resolve=>resolve()).then(()=>{console.log(5)});

}

run();

我的预期输出是按顺序打印 1,2,3,4,5 的数字,但我得到的是:
1
2
3
5
4

不出所料,第一个 nextTick在第一个 .then 之前进行评估回调,因为 process.nextTick.then都推迟到 future 的报价, process.nextTick.then 之前声明.所以 12按预期顺序输出。

代码不应达到 await 之后的内容直到 .then 之后已解决,并且按预期工作,如 3在预期的地方输出。

然后基本上我们重复了代码的第一部分,但这次是 .thenprocess.nextTick 之前调用.

这似乎是不一致的行为。为什么 process.nextTick.then 之前被调用第一次回调但不是第二次?

最佳答案

node.js 事件队列不是单个队列。它实际上是一堆不同的队列,比如 process.nextTick()并 promise .then()处理程序不在同一个队列中处理。所以,不同类型的事件不一定是先进先出的。

因此,如果您有多个事件大约在同一时间进入事件队列,并且您希望它们以特定顺序服务,那么保证该顺序的最简单方法是编写代码以强制执行您想要的顺序,而不是尝试准确猜测两件同时进入队列的事情将如何排序。

确实有两个完全相同类型的操作,例如两个 process.nextTick()操作或两个已解决的 promise 操作将按照它们放入事件队列的顺序进行处理。但是,不同类型的操作可能不会按照它们被放入事件队列的顺序进行处理,因为在事件循环通过所有不同类型的事件进行的循环中,不同类型的事件是在不同的时间处理的。

有可能完全理解 node.js 中的事件循环如何针对每种类型的事件工作,并准确预测大约同时进入事件队列的两个事件将如何相对于彼此进行处理,但事实并非如此简单的。当新事件被添加到事件队列时,它还取决于事件循环在其当前处理中的位置,这一事实使情况变得更加复杂。

就像我之前评论中的交付示例一样,新交付相对于其他交付的确切处理时间取决于新订单到达队列时交付驱动程序的位置。 node.js 事件系统也是如此。如果在 node.js 处理计时器事件时插入新事件,则它与其他类型事件的相对顺序可能与 node.js 在插入时处理文件 I/O 完成事件时不同。因此,由于这种显着的复杂性,我不建议尝试预测几乎同时插入事件队列的不同类型的异步事件的执行顺序。

而且,我应该补充一点,原生 Promise 直接插入到事件循环实现中(作为它们自己的微任务类型),因此原生 Promise 实现在原始代码中的行为可能与非原生 Promise 实现不同。这也是不尝试准确预测事件循环将如何安排不同类型的事件相对于彼此的原因。

如果处理顺序对您的代码很重要,则使用代码强制执行特定的完成处理顺序。

作为将事件插入事件队列时事件队列所做的事情的一个示例,您的代码简化为:

async function run(){

process.nextTick(()=>{
console.log(1);
});

await Promise.resolve().then(()=>{console.log(2)});

console.log(3);

process.nextTick(()=>{
console.log(4);
});

Promise.resolve().then(()=>{console.log(5)});

}

run();

生成此输出:
1
2
3
5
4

但是,只需更改 run()被称为 Promise.resolve().then(run)顺序突然不同了:
async function run(){

process.nextTick(()=>{
console.log(1);
});

await Promise.resolve().then(()=>{console.log(2)});

console.log(3);

process.nextTick(()=>{
console.log(4);
});

Promise.resolve().then(()=>{console.log(5)});

}

Promise.resolve().then(run);

生成这个完全不同的输出:
2
3
5
1
4

您可以看到,当代码从已解决的 promise 开始时,该代码中发生的其他已解决 promise 会在 .nextTick() 之前得到处理。当代码从事件队列处理中的不同点启动时,情况并非如此。这是使事件队列系统非常难以预测的部分。

因此,如果您试图保证特定的执行顺序,您必须使用所有相同类型的事件,然后它们将按彼此相对的 FIFO 顺序执行,或者您必须让您的代码强制执行您想要的执行顺序.所以,如果你真的想看到这个顺序:
1
2
3
4
5

您可以使用本质上映射到此的所有 promise :
async function run(){

Promise.resolve().then(() => {
console.log(1);
})
Promise.resolve().then(() => {
console.log(2)
});

await Promise.resolve().then(()=>{});

console.log(3);

Promise.resolve().then(() => {
console.log(4)
});

Promise.resolve().then(()=>{console.log(5)});

}

run();

或者,您更改​​代码的结构,以便代码使其始终以所需的顺序处理事物:
async function run(){

process.nextTick(async ()=>{
console.log(1);
await Promise.resolve().then(()=>{console.log(2)});
console.log(3);
process.nextTick(()=>{
console.log(4);
Promise.resolve().then(()=>{console.log(5)});
});
});
}

run();

最后两种情况中的任何一种都将生成输出:
1
2
3
4
5

关于javascript - 意外行为将 process.nextTick 与 async/await 混合。事件循环在这里如何工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53138464/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com