Using Async/Await With a Foreach Loop

Using async/await with a forEach loop

Sure the code does work, but I'm pretty sure it doesn't do what you expect it to do. It just fires off multiple asynchronous calls, but the printFiles function does immediately return after that.

Reading in sequence

If you want to read the files in sequence, you cannot use forEach indeed. Just use a modern for … of loop instead, in which await will work as expected:

async function printFiles () {
const files = await getFilePaths();

for (const file of files) {
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}
}

Reading in parallel

If you want to read the files in parallel, you cannot use forEach indeed. Each of the async callback function calls does return a promise, but you're throwing them away instead of awaiting them. Just use map instead, and you can await the array of promises that you'll get with Promise.all:

async function printFiles () {
const files = await getFilePaths();

await Promise.all(files.map(async (file) => {
const contents = await fs.readFile(file, 'utf8')
console.log(contents)
}));
}

await with array foreach containing async await

Use Array.prototype.map and Promise.all:

async function procesMultipleCandidates (data) {
let generatedResponse = []
await Promise.all(data.map(async (elem) => {
try {
// here candidate data is inserted into
let insertResponse = await insertionInCandidate(elem)
// and response need to be added into final response array
generatedResponse.push(insertResponse)
} catch (error) {
console.log('error'+ error);
}
}))
console.log('complete all') // gets loged first
return generatedResponse // return without waiting for process of
}

Or use a for/of loop if you don't want the loop run concurrently:

async function procesMultipleCandidates (data) {
let generatedResponse = []
for(let elem of data) {
try {
// here candidate data is inserted into
let insertResponse = await insertionInCandidate(elem)
// and response need to be added into final response array
generatedResponse.push(insertResponse)
} catch (error) {
console.log('error'+ error);
}
}
console.log('complete all') // gets loged first
return generatedResponse // return without waiting for process of
}

What is the difference between async/await forEach and Promise.all + map

For Googlers who only saw the question title

Don't use async/await with forEach. Either use a for-of loop, or use Promise.all() with array.map().

If you have a general understanding on promises and async/await, the TL;DR on the differences between promise.all() + array.map() and .forEach(), is that it's impossible to await a forEach() itself. Yes, you can run tasks in parallel in .forEach() just like you can with .map(), but you can't wait for all of those parallel tasks to finish, then do something once they've all finished. The whole point of using .map() instead of .forEach() is so you can get a list of promises, collect them with Promise.all(), then await the whole thing. To see what I mean, just put a console.log('Finished') after a forEach(async () => ...), and you'll see the "finished" get logged out before everything has finished running in the .forEach() loop. My advice would be to just not use .forEach() with async logic (and really there's not a reason use ever use .forEach() anymore these days, as I explain further down).

For those who need something a bit more in depth, the rest of this answer will dive into more detail, first giving a small review on promises, then showing an in-depth explanation on how these approaches behave differently and why .forEach() is always the inferior solution when it comes to async/await.

A primer on promises and async/await

For the purposes of this discussion, you just have to remember that a promise is a special object that's promising that some task is going to be completed at some point in the future. You can attach listeners to a promise via .then(), to get notified when the task is completed, and to receive the resolved value.

An async function is simply a function that will always return a promise, no matter what. Even if you do async function doThing() { return 2 }, it's not going to return 2, it's going to return a promise that immediately resolves to the value 2. Note that an asynchronous function will always return a promise immediately, even if it takes a long time for the function to run. This is why it's called a "promise", it's promising that the function will eventually finish running, and if you want to be notified for when the function has finished, you can add an event listener to it, via .then() or await.

await is special syntax that lets you pause execution of an async function until a promise resolves. await will only effect the function it's directly inside. Behind the scenes, await is simply adding a special event listener to the promise's .then(), so it can know when the promise resolves and what value it resolves with.

async fn1() {
async fn2() {
await myPromise // This pauses execution of fn2(), not fn1()!
}
...
}

async function fn1() {
function fn2() {
await myPromise // An error, because fn2() is not async.
}
...
}

If you can get a good grasp of these principles, then you should be able to understand the next sections.

for-of

A for-of loop lets you execute your asynchronous tasks in serial, one after another. For example:

const delays = [1000, 1400, 1200];

// A function that will return a
// promise that resolves after the specified
// amount of time.
const wait = ms => new Promise(resolve => setTimeout(resolve, ms))

async function main() {
console.log('start')
for (const delay of delays) {
await wait(delay)
console.log('Finished waiting for the delay ' + delay)
}
console.log('finish')
}

main()

Write an async method with Parallel.Foreach loop that does call another async method to pull record

Well for one thing you can pretend that Parallel.ForEach awaits your async functions, but it doesn't. Instead you want to write something like this:

   await Task.WhenAll(customers.Select(async customer =>
{
var processedCustomer = await MethodB(customer);
inboundCustomersFiles.AddRange(processedCustomer);
}));

Task.WhenAll behaves like Parallel.ForEach, but it's awaitable and it also awaits every task you pass to it before completing its own task. Hence when your await Task.WhenAll completes, all the inner tasks have completely completed as well.

the process in methodB customerRecord takes time

That is very ambiguous. If you mean it takes server and/or IO time then that's fine, that's what async is for. If you mean it takes your CPU time (ie it processes data locally for a long time), then you should spin up a task on a thread pool and await its completion. Note that this is not necessarily the default thread pool! Especially if you're writing an ASP.NET (core) application, you want a dedicated thread pool just for this stuff.

Use async await in forEach loop, node js

.forEach() is not async-aware. Use a plain for/of loop instead. In fact, pretty much stop using .forEach() entirely these days as its basically obsolete. With block scope available now with let and const, there is no reason to use .forEach() and it's extra function scope any more. Use a regular for loop instead.

You can fix this particular code by changing to this:

exports.filterAverageRatingsEachSkillForStudent = async (req, res) => {
let receiver = req.query.receiver; //array of id ['ABBd23', 'SDSd242', 'dfNJ47']
try {
console.log('receiver id', receiver);

for (let id of reciever) {
console.log('Query String ', id);
const findRating = await FeedbackSchema.find({
receiver: id,
}).select('ratingL ratingS ratingR ratingW');
}
} catch (error) {
console.log(error.message);
}
}

It also appears that you must not have disclosed all the code for this function because it looks like you're trying to get findRating, but then you never do anything with it.

The error you report about cannot set headers after they are sent does not occur in this code since that error is about sending multiple responses to the same incoming request and you don't send any responses in this code. So, help with that particular error would require disclosing more code or perhaps fixing this function will also fix that (we can't tell since we can't see the code that actually causes that error).

How to return data in foreach loop having multiple async functions inside

Try using Promise.all

Something like this =>

let result = await prepareStocks(data);
async function prepareStocks(incomingStocks) {
var stockCodes = incomingStocks.stocks.split(',');
var stockPrices = incomingStocks.trigger_prices.split(',');
var alertName = incomingStocks.alert_name;


const STOCKS = await Promise.all(stockCodes.map(async (stocks, index) => {
if (stockPrices[index] > 100) {

var stockCodes = {
code: stocks,
price: stockPrices[index],
orderType: (urls.buy.includes(alertName) ? 'BUY' : 'WATCH'),
target: await setSellPrice(stockPrices[index], 1),
stopLoss: await setStopLoss(stockPrices[index], 1),
}
return stockCodes;
}
})

return STOCKS;
}

How to parallel process multiple async task foreach loop

Since you are not awaiting the call to GetIsConnected() in your AFTER example, the loop will continue and the value may or may not be checked against what it would be once the call to GetIsConnected() is finished, what I think you'd want to do is add another method with a return type of Task and let the method call GetIsConnected() and await it inside the method then call GetCarDetailsAsync() if necessary:

//changed to return type of Task, as async void should be avoided
//unless this is an event handler method
private async Task Refresh()
//private async void Refresh() //change to this if event handler
{
List<Task> listOfTasks = new List<Task>();
foreach (var item in Cars.ToList<Carnet>())
{
listOfTasks.Add(GetCarDetails(item));
}
await Task.WhenAll(listOfTasks).ConfigureAwait(false);
}

//method to await the call to GetIsConnect()
//and then call GetCarDetailsAsync() if necessary
private async Task GetCarDetails(Carnet c)
{
await c.GetIsConnected();
if (c.IsConnected)
{
await c.GetCarDetailsAsync();
}
}

how to wait for async function in foreach loop

forEach method doesn't wait for the async operation to end before moving on to the next iteration.

You can use async-await syntax along with for of loop

async function foo() {
console.log('1');

for (const value of msg.reactions.cache) {
const data = await value.users.fetch();
console.log(data);
}

console.log('3');
}

foo();


Related Topics



Leave a reply



Submit