Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to await a list of tasks asynchronously using LINQ?

I have a list of tasks that I created like this:

public async Task<IList<Foo>> GetFoosAndDoSomethingAsync() {     var foos = await GetFoosAsync();      var tasks = foos.Select(async foo => await DoSomethingAsync(foo)).ToList();      ... } 

By using .ToList(), the tasks should all start. Now I want to await their completion and return the results.

This works in the above ... block:

var list = new List<Foo>(); foreach (var task in tasks)     list.Add(await task); return list; 

It does what I want, but this seems rather clumsy. I'd much rather write something simpler like this:

return tasks.Select(async task => await task).ToList(); 

... but this doesn't compile. What am I missing? Or is it just not possible to express things this way?

like image 561
Matt Johnson-Pint Avatar asked Feb 18 '14 23:02

Matt Johnson-Pint


People also ask

Is there a way to use LINQ with async code?

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: which is quite nice. WhenAll returns an array, so I believe your method can return the results directly:

How does the while loop work with LINQ processurlasync?

Each call to ProcessUrlAsync in the following code returns a Task<TResult>, where TResult is an integer: Due to deferred execution with the LINQ, you call Enumerable.ToList to start each task. The while loop performs the following steps for each task in the collection:

How do you start a while loop in LINQ?

Due to deferred execution with the LINQ, you call Enumerable.ToList to start each task. The while loop performs the following steps for each task in the collection: Awaits a call to WhenAny to identify the first task in the collection that has finished its download.

What does it mean when a query is asynchronous?

It means that the query is blocking. So it is not really asynchronous. will first start an asynchronous operation for each event. Then this line: will wait for those operations to complete one at a time (first it waits for the first event's operation, then the next, then the next, etc).


2 Answers

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); 
like image 160
Stephen Cleary Avatar answered Oct 02 '22 19:10

Stephen Cleary


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);         }     } } 
like image 45
Clement Avatar answered Oct 02 '22 19:10

Clement