使用async await与Array.map

时间:2016-10-19 19:39:06

标签: javascript typescript promise async-await ecmascript-2017

给出以下代码:

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&lt; number&gt; []'不能分配给'number []'。   输入'Promise&lt; number&gt;不能指定为“数字”类型。

我该如何解决?如何让async awaitArray.map一起工作?

8 个答案:

答案 0 :(得分:273)

这里的问题是你试图await一系列承诺而不是承诺。这不符合你的期望。

当传递给await的对象不是Promise时,await只是立即返回值,而不是尝试解析它。所以,既然你在这里传递了await数组(Promise对象)而不是Promise,那么await返回的值就是那个数组Promise<number>[]的数组。

您需要在Promise.all返回的数组上调用map,以便在await之前将其转换为单个Promise。

根据MDN docs for Promise.all

  

Promise.all(iterable)方法返回一个解析的promise   当迭代参数中的所有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;
}));

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

答案 1 :(得分:11)

如果您没有使用本机Promise而是使用Bluebird,那么还有另一种解决方案。

你也可以尝试使用Promise.map(),混合array.map和Promise.all

在你的情况下:

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

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

答案 2 :(得分:6)

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

答案 3 :(得分:1)

您可以使用:

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

答案 4 :(得分:0)

下面的解决方案以异步方式处理数组的所有元素并保留顺序:

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 p = arr.map(n => calc(n));
  const results = await Promise.all(p);
};

asyncFunc();

codepen

答案 5 :(得分:0)

我在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
  }

注意:图像(实体)没有属性网址,但ImageResponse-具有

答案 6 :(得分:0)

使用 modern-async's map() 的解决方案:

import { map } from 'modern-async'

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

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

答案 7 :(得分:-2)

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

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