对Array.map使用异步等待

给出以下代码:

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>不可分配给类型'number'。

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

留姬小次郎2020/05/28 14:53:11

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

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

asyncFunc();

codepen

请注意,我们仅“等待” Promise.all。我们多次调用calc而不使用“ await”,并且立即收集了一系列未解决的Promise。然后Promise.all等待所有它们的解析,并按顺序返回具有已解析值的数组。

斯丁前端2020/05/28 14:53:11

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

别太浪2020/05/28 14:53:11

这里的问题是,您尝试await的是承诺而不是承诺。这没有达到您的期望。

When the object passed to await is not a Promise, await simply returns the value as-is immediately instead of trying to resolve it. So since you passed await an array (of Promise objects) here instead of a Promise, the value returned by await is simply that array, which is of type Promise<number>[].

What you need to do here is call Promise.all on the array returned by map in order to convert it to a single Promise before awaiting it.

According to the MDN docs for Promise.all:

The Promise.all(iterable) method returns a promise that resolves when all of the promises in the iterable argument have resolved, or rejects with the reason of the first passed promise that rejects.

So in your case:

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;
}));

This will resolve the specific error you are encountering here.

飞云2020/05/28 14:53:11

如果您不使用本地Promises,而是使用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;
  });
十刃2020/05/28 14:53:11

我建议如上所述使用Promise.all,但是如果您真的想避免这种方法,则可以执行for或任何其他循环:

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