Search code examples
node.jsexpressmongoose

How to store async/await map() result in a variable?


I want to store the result of an async map() function that walk through an array of ids and use the Todo.findById(todo.id) method to retrieve the actual todo object, here's my code :

    const todoIds = ["6325a0fa4d1b728890d90d98", "6325a0fa4d1b728890d90d97", "6325a0fa4d1b728890d90d96"];

    const tasks = await todoIds.map(async (id) => {
      const task = await Todo.findById(id);
      return task;
    });

But here's the console.log(tasks) :

[ Promise { <pending> }, Promise { <pending> }, Promise { <pending> } ]

Solution

  • An asynchronous function f is a function that returns a promise, awaiting the result of the function gives the result to which that promise eventually resolves. In other words

    • const task = f(id) gives a promise
    • const task = await f(id) gives a result.

    todoIds.map(f) does not await the results of the asychronous function f. In other words, it invokes f for the first entry in todoIds and then for the second entry, without waiting for the first invocation to come back with a result.

    Therefore you get back an array with three promises (corresponding to the first bullet point above). In order to convert that into an array with three results, use

    const taskResults = await Promise.all(tasks);
    

    This will execute all three Todo.findById in parallel, whereas in Ipizzinidev's solution (meanwhile deleted?), they are executed consecutively.

    Addendum: Other functions operating on arrays, such as todoIds.forEach(f), todoIds.some(f) and todoIds.every(f) have the same behavior, they do not await the results of an asynchronous function f either. Worse, since todoIds.some(f) and todoIds.every(f) evaluate the boolean return values of f, the promise returned by f counts as true, leading to an unexpected outcome.