在调试一个设置以控制某些CPU密集型异步任务的承诺并发时,我遇到了以下我无法理解的行为。
我控制流程的方法是首先创建一个Promises数组,然后通过显式等待来控制这些Promises的执行。我生成了以下代码片段,显示了意外的行为:
import fs from 'node:fs';
import { setTimeout } from 'node:timers/promises';
async function innerAsync(n: number) {
return fs.promises.readdir('.').then(value => {
console.log(`Executed ${n}:`, value);
return value;
});
}
async function controllerAsync() {
const deferredPromises = new Array<Promise<string[]>>();
for (const n of [1, 2, 3]) {
const defer = innerAsync(n);
deferredPromises.push(defer);
}
const result = deferredPromises[0];
return result;
}
const result = await controllerAsync();
console.log('Resolved:', result);
await setTimeout(1000);
此代码段生成以下结果(假设.
中有2个文件):
Executed 1: [ 'test.txt', 'test2.txt' ]
Resolved: [ 'test.txt', 'test2.txt' ]
Executed 2: [ 'test.txt', 'test2.txt' ]
Executed 3: [ 'test.txt', 'test2.txt' ]
观察到
在第15行定义的承诺都被执行,而不管它们是否被返回/等待(2和3)。
问题是什么原因导致其他看似未使用的承诺被执行?如何确保在此代码片段中仅执行第一个承诺,而其他承诺保持待定状态?
- 节点v19.2
- typescript v4.9.4
- StackBlitz
2条答案
按热度按时间zzzyeukh1#
是什么原因导致另一个看似未被使用的承诺被执行?
promise是一种用于监视 * 某事 * 并提供API以响应 * 某事 * 完成的工具。
innerAsync
函数调用fs.promises.readdir
,调用fs.promises.readdir
会发生一些事情(阅读目录)。then
回调函数是在阅读该目录完成时作为响应调用的。(It不清楚为什么将
innerAsync
标记为async
,然后没有在其中使用await
。)如果您不使用
then
或await
,那么这不会改变您调用fs.promises.readdir
的事实!如何确保在此代码片段中,只有第一个Promise被执行,而其他Promise仍处于挂起状态?
专注于做事情的函数,而不是处理它的代码是否完整。
或者打个比方:
如果你告诉爱丽丝去商店买牛奶,但不告诉鲍勃在爱丽丝回来时把牛奶放进冰箱,那么你就不应该对爱丽丝去商店买牛奶回来感到惊讶。
db2dz4w82#
承诺根本不是“执行”的。承诺是一种观察已经**在进行中的异步进程的方式。**当您有一个承诺时,它报告结果的进程已经在进行中。¹是对
fs.readdir
的调用启动了进程,而不是对承诺调用then
。(即使是这样,您也会立即在innerAsync
函数中根据fs.readdir
的承诺调用then
。)如果你想等待开始操作,等待调用方法开始操作,并给你承诺,我会显示你的代码的编辑版本,但它不清楚我应该做什么,特别是因为
controllerAsync
只看数组的第一个元素,并不等待任何事情完成后返回.还有几点注意事项:
1.将
async
/await
与.then
的显式调用结合起来几乎没有用,如innerAsync
中那样,该函数可以更清楚地写成:从操作顺序的Angular 来看,这与带有显式
then
的版本做的事情完全相同。1.将
controllerAsync
声明为async
函数没有任何意义,因为它从来不使用await
。创建async
函数的唯一原因是可以在其中使用await
。¹这在正常情况下是正确的(绝大多数情况下),包括您从Node.js的
fs/promises
方法中获得承诺的情况。(或曾经),不幸的是,有两个库,它们有返回promise的函数,但直到promise的then
方法被调用时才开始实际工作。但这些都是利基离群值,可以说违反了承诺的语义。