Async/Await inside Array#map()
JavascriptAsync AwaitEcmascript 2017Javascript Problem Overview
I'm getting compile time error with this code:
const someFunction = async (myArray) => {
return myArray.map(myValue => {
return {
id: "my_id",
myValue: await service.getByValue(myValue);
}
});
};
Error message is:
> await is a reserved word
Why can't I use it like this?
I also tried another way, but it gives me same error:
const someFunction = async (myArray) => {
return myArray.map(myValue => {
const myNewValue = await service.getByValue(myValue);
return {
id: "my_id",
myValue: myNewValue
}
});
};
Javascript Solutions
Solution 1 - Javascript
You can't do this as you imagine, because you can't use await
if it is not directly inside an async
function.
The sensible thing to do here would be to make the function passed to map
asynchronous. This means that map
would return an array of promises. We can then use Promise.all
to get the result when all the promises return. As Promise.all
itself returns a promise, the outer function does not need to be async
.
const someFunction = (myArray) => {
const promises = myArray.map(async (myValue) => {
return {
id: "my_id",
myValue: await service.getByValue(myValue)
}
});
return Promise.all(promises);
}
Solution 2 - Javascript
If you want to run map with an asynchronous mapping function you can use the following code:
const resultArray = await Promise.all(inputArray.map(async (i) => someAsyncFunction(i)));
How it works:
inputArray.map(async ...)
returns an array of promises - one for each value ininputArray
.- Putting
Promise.all()
around the array of promises converts it into a single promise. - The single promise from
Promise.all()
returns an array of values - the individual promises each resolve to one value. - We put
await
in front ofPromise.all()
so that we wait for the combined promise to resolve and store the array of resolved sub-promises into the variableresultArray
.
In the end we get one output value in resultArray
for each item in inputArray
, mapped through the function someAsyncFunction
. We have to wait for all async functions to resolve before the result is available.
Solution 3 - Javascript
That's because the function in map
isn't async, so you can't have await in it's return statement. It compiles with this modification:
const someFunction = async (myArray) => {
return myArray.map(async (myValue) => { // <-- note the `async` on this line
return {
id: "my_id",
myValue: await service.getByValue(myValue)
}
});
};
So… it's not possible to give recommendation without seeing the rest of your app, but depending on what are you trying to do, either make the inner function asynchronous or try to come up with some different architecture for this block.
Update: we might get top-level await one day: https://github.com/MylesBorins/proposal-top-level-await
Solution 4 - Javascript
it will be 2 instructions, but just shift "await" with the extra instruction
let results = array.map((e) => fetch('....'))
results = await Promise.all(results)
Solution 5 - Javascript
I tried all these answers but no one works for my case because all answers return a promise
object not the result of the promise
like this:
{
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: Array(3)
0: ...an object data here...
1: ...an object data here...
2: ...an object data here...
length: 3
[[Prototype]]: Array(0)
}
Then I found this answer https://stackoverflow.com/a/64978715/8339172 that states if map
function is not async or promise aware.
So instead of using await
inside map
function, I use for
loop and await
the individual item because he said that for
loop is async
aware and will pause the loop.
Solution 6 - Javascript
When you want each remapped value resolved before moving on to the next, you can process the array as an asynchronous iterable.
Below, we use library iter-ops, to remap each value into promise, and then produce an object with resolved value, because map
itself shouldn't be handling any promises internally.
import {pipe, map, wait, toAsync} from 'iter-ops';
const i = pipe(
toAsync(myArray), // make asynchronous
map(myValue => {
return service.getByValue(myValue).then(a => ({id: 'my_id', myValue: a}))
}),
wait() // wait for each promise
);
(async function() {
for await (const a of i) {
console.log(a); // print resulting objects
}
})
After each value, we use wait to resolve each remapped value as it is generated, to keep resolution requirement consistent with the original question.