Use async await with Array.map

277,740

Solution 1

The problem here is that you are trying to await an array of promises rather than a Promise. This doesn't do what you expect.

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 probably want to do 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.

Depending on exactly what it is you're trying to do you may also consider using Promise.allSettled, Promise.any, or Promise.race instead of Promise.all, though in most situations (almost certainly including this one) Promise.all will be the one you want.

Solution 2

Solution below to properly use async await and Array.map together. Process all elements of the array in parallel, asynchronously AND preserve the order:

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

Also codepen.

Notice we only "await" for Promise.all. We call calc without "await" multiple times, and we collect an array of unresolved promises right away. Then Promise.all waits for resolution of all of them and returns an array with the resolved values in order.

Solution 3

There's another solution for it if you are not using native Promises but Bluebird.

You could also try using Promise.map(), mixing the array.map and Promise.all

In you case:

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

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

Solution 4

If you map to an array of Promises, you can then resolve them all to an array of numbers. See Promise.all.

Solution 5

You can use:

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

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

If you wish to use Promise.all() instead you can go for Promise.allSettled() So you can have better control over rejected promises.

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

Share:
277,740
Alon
Author by

Alon

Updated on July 08, 2022

Comments

  • Alon
    Alon almost 2 years

    Given the following code:

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

    which produces the following error:

    TS2322: Type 'Promise<number>[]' is not assignable to type 'number[]'. Type 'Promise<number> is not assignable to type 'number'.

    How can I fix it? How can I make async await and Array.map work together?