在Array.map中使用async await

给定以下代码:

var arr = [1,2,3,4,5];


var results: number[] = await arr.map(async (item): Promise<number> => {
await callAsynchronousOperation(item);
return item + 1;
});

这会产生以下错误:

TS2322:类型“Promise<number>[]”不能分配给类型“number[]”。 类型的Promise< number>

我该怎么解决呢?如何使async awaitArray.map一起工作?

394981 次浏览

如果映射到promise数组,则可以将它们全部解析为数字数组。看到Promise.all

这里的问题是你试图await一个承诺数组,而不是一个承诺。这并不是你所期望的。

当传递给await的对象不是Promise时,await只是立即返回值,而不是试图解析它。因此,由于你在这里传递给await的是一个(Promise对象的)数组而不是一个Promise, await返回的值就是那个数组,它的类型是Promise<number>[]

你可能想做的是对map返回的数组调用Promise.all,以便在awaiting它之前将它转换为单个Promise。

根据Promise.all的MDN文档:

Promise.all(iterable)方法返回一个可解析的承诺 当iterable实参中的所有promise都已解决时,或者

.

.

在你的例子中:

var arr = [1, 2, 3, 4, 5];


var results: number[] = await Promise.all(arr.map(async (item): Promise<number> => {
await callAsynchronousOperation(item);
return item + 1;
}));

这将解决您在这里遇到的特定错误。

根据你想要做的具体内容,你也可以考虑使用Promise.allSettledPromise.anyPromise.race来代替Promise.all,尽管在大多数情况下(几乎肯定包括这种情况)Promise.all将是你想要的。

我推荐使用Promise。如上所述,但如果你真的想避免这种方法,你可以做一个for循环或任何其他循环:

const arr = [1,2,3,4,5];
let resultingArr = [];
for (let i in arr){
await callAsynchronousOperation(i);
resultingArr.push(i + 1)
}

仅供参考:如果你想迭代数组的项,而不是索引(@ralfoide的评论),在let i in arr语句中使用of而不是in

如果你使用的不是本地承诺而是蓝鸟,还有另一个解决方案。

你也可以尝试使用Promise.map (),混合数组。地图和承诺。所有

对你来说:

  var arr = [1,2,3,4,5];


var results: number[] = await Promise.map(arr, async (item): Promise<number> => {
await callAsynchronousOperation(item);
return item + 1;
});

解决方案下面正确使用异步等待和数组。地图在一起。并行、异步处理数组中的所有元素,并保持顺序:

const arr = [1, 2, 3, 4, 5, 6, 7, 8];


const randomDelay = () => new Promise(resolve => setTimeout(resolve, Math.random() * 1000));


const calc = async n => {
await randomDelay();
return n * 2;
};


const asyncFunc = async() => {
const unresolvedPromises = arr.map(calc);
const results = await Promise.all(unresolvedPromises);
document.write(results);
};


document.write('calculating...');
asyncFunc();

codepen

注意我们只“等待”;Promise.all。我们调用calc时不需要“等待”;多次,我们马上收集到一系列未解决的承诺。然后承诺。All等待解析所有这些值,并返回一个包含已解析值的数组。

我在BE方面有一个任务,从回购中找到所有实体,并添加一个新的属性url并返回到控制器层。这就是我如何做到的(感谢Ajedi32的回应):

async findAll(): Promise<ImageResponse[]> {
const images = await this.imageRepository.find(); // This is an array of type Image (DB entity)
const host = this.request.get('host');
const mappedImages = await Promise.all(images.map(image => ({...image, url: `http://${host}/images/${image.id}`}))); // This is an array of type Object
return plainToClass(ImageResponse, mappedImages); // Result is an array of type ImageResponse
}

请注意: Image (entity)没有属性url,但imagerresponse -有

你可以使用:

for await (let resolvedPromise of arrayOfPromises) {
console.log(resolvedPromise)
}

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of

如果你想使用Promise.all()代替,你可以使用Promise.allSettled() 所以你可以更好地控制被拒绝的承诺

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled

使用modern-async的地图()的解决方案:

import { map } from 'modern-async'


...
const result = await map(myArray, async (v) => {
...
})

使用该库的优点是可以使用mapLimit ()mapSeries ()来控制并发性。

这是最简单的方法。

await Promise.all(
arr.map(async (element) => {
....
})
)