I'm trying to use async
and await
in a function that uses a forEach
loop. Annoyingly I can't get it to work. What should happen is it takes an array of event docs
, loops through them, adds in some extra data, then pushes them to the events
array. This events
array is then returned from the original function. Here is my code:
async function getEvents() {
...
var events = []
await addExtrasToDocsForUser(docs, currentUserId, events)
return events
}
var addExtrasToDocsForUser = (docs, currentUserId, events) => {
return docs.forEach(async (eventDoc) => {
const event = await addExtrasToDocForUser(eventDoc, currentUserId)
events.push(event)
})
}
What actually happens is the getEvents()
function returns events
as an empty array before the forEach
loop has completed. How do I fix this?
forEach is not designed for asynchronous code. (It was not suitable for promises, and it is not suitable for async-await.) For example, the following forEach loop might not do what it appears to do: const players = await this.
When evaluate for loop, we have await promise inside the async function, the execution will pause until the await promise is settled. So, you can think of that the files are read one by one in a determined order. Sometimes, we really need the the async functions to be executed in a sequential order.
You need to place the loop in an async function, then you can use await and the loop stops the iteration until the promise we're awaiting resolves. You could also use while or do.. while or for loops too with this same structure. But you can't await with Array.
If a Promise is passed to an await expression, it waits for the Promise to be fulfilled and returns the fulfilled value.
Basically, this is what happens inside forEach
:
Array.prototype.forEach = function (callback) {
for (let index = 0; index < this.length; index++) {
callback(this[index], index, this);
}
};
Actually, the real implementation is the following, but the bottom line is that we are not waiting for the callback to be done, so using a function that returns a promise won't wait for the promise to resolve everytime.
Your code is not complete and verifiable so I can't be sure that the following works, but it probably should act as you'd expect:
const addExtrasToDocsForUser = async (docs, currentUserId, events) => {
for (let i=0; i<docs.length; i++) {
const event = await addExtrasToDocForUser(docs[i], currentUserId);
events.push(event);
}
return;
}
You might also want to check this CodeBurst article on foreach + async/await
Use Promise.all
and map
get all inner promises and return a single one that awaits them all:
const addExtrasToDocsForUser = async (docs, currentUserId, events) => {
return Promise.all(docs.map(async (eventDoc) => {
const event = await addExtrasToDocForUser(eventDoc, currentUserId)
events.push(event)
}));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With