Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Any point to List<T>.ForEach() with Async?

I ran into this piece of code:

items.ForEach(async item =>
{
     doSomeStuff();   
     await mongoItems.FindOneAndUpdateAsync(mongoMumboJumbo);
     await AddBlah(SqlMumboJumbo);
});

Is there any point in making this a .forEach delegate, or could it be just a normal foreach loop? As long as the function that contains the loop is in is async, this would be async by default?

like image 251
VSO Avatar asked Aug 11 '15 17:08

VSO


People also ask

Does forEach work with async?

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.

Why async does not work in forEach?

forEach expects a synchronous function and won't do anything with the return value. It just calls the function and on to the next. for...of will actually await on the result of the execution of the function.

Is forEach async in C#?

ForEach doesn't play particularly well with async (neither does LINQ-to-objects, for the same reasons). In this case, I recommend projecting each element into an asynchronous operation, and you can then (asynchronously) wait for them all to complete.

Is Lodash forEach async?

forEach loop is not asynchronous. The Array. prototype. forEach method accepts a callback as an argument which can be an asynchronous function, but the forEach method will not wait for any promises to be resolved before moving onto the next iteration.


2 Answers

The delegate received by ForEach is an Action<T>:

public void ForEach(Action<T> action)

This means, that any async delegate you use inside it will effectively turn into an async void method. These are "fire and forget" style of execution. This means your foreach wont finish asynchronously waiting before continuing to invoke the delegate on the next item in the list, which might be an undesired behavior.

Use regular foreach instead.

Side note - foreach VS ForEach by Eric Lippert, great blog post.

like image 133
Yuval Itzchakov Avatar answered Oct 12 '22 04:10

Yuval Itzchakov


You don't know when your function is finished, nor the result of the function. If you start each calculation in a separate Task, you can await Task.WhenAll and interpret the results, even catch exceptions:

private async Task ActionAsync(T item)
{
    doSomeStuff();   
    await mongoItems.FindOneAndUpdateAsync(mongoMumboJumbo);
    await AddBlah(SqlMumboJumbo);
}

private async Task MyFunction(IEnumerable<T> items)
{
    try
    {
        foreach (var item in items)
        {
            tasks.Add( ActionAsync(item) )
        }
        // while all actions are running do something useful
        // when needed await for all tasks to finish:
        await Task.WhenAll(tasks);
        // interpret the result of each action using property Task.Result
    }
    catch (AggregateException exc)
    {
        ProcessAggregateException(exc);
    }
}

The aggregateException is thrown when any of your task throws an exception. If contains all exceptions thrown by all your tasks.

like image 41
Harald Coppoolse Avatar answered Oct 12 '22 06:10

Harald Coppoolse