How to await a list of tasks asynchronously using LINQ?
LINQ doesn't work perfectly with async
code, but you can do this:
var tasks = foos.Select(DoSomethingAsync).ToList();
await Task.WhenAll(tasks);
If your tasks all return the same type of value, then you can even do this:
var results = await Task.WhenAll(tasks);
which is quite nice. WhenAll
returns an array, so I believe your method can return the results directly:
return await Task.WhenAll(tasks);
To expand on Stephen's answer, I've created the following extension method to keep the fluent style of LINQ. You can then do
await someTasks.WhenAll()
namespace System.Linq
{
public static class IEnumerableExtensions
{
public static Task<T[]> WhenAll<T>(this IEnumerable<Task<T>> source)
{
return Task.WhenAll(source);
}
}
}
One issue with Task.WhenAll
is that it would create a parallelism. In most of the cases it might be even better, but sometimes you want to avoid it. For example, reading data in batches from DB and sending data to some remote web service. You don't want to load all the batches to the memory but hit the DB once the previous batch has been processed. So, you have to break the asynchronicity. Here is an example:
var events = Enumerable.Range(0, totalCount/ batchSize)
.Select(x => x*batchSize)
.Select(x => dbRepository.GetEventsBatch(x, batchSize).GetAwaiter().GetResult())
.SelectMany(x => x);
foreach (var carEvent in events)
{
}
Note .GetAwaiter().GetResult()
converting it to synchronous. DB would be hit lazily only once batchSize
of events have been processed.
Use Task.WaitAll
or Task.WhenAll
whichever is approriate.
Task.WhenAll should do the trick here.