The following snippet compiles, but I'd expect it to await the task result instead of giving me a List<Task<T>>
.
var foo = bars.Select(async bar => await Baz(bar)).ToList()
As pointed out here, you need to use Task.WhenAll
:
var tasks = foos.Select(async foo => await DoSomethingAsync(foo)).ToList();
await Task.WhenAll(tasks);
But a comment points out that the async
and await
inside the Select()
is not needed:
var tasks = foos.Select(foo => DoSomethingAsync(foo)).ToList();
A similar question here where someone tries to use an async method inside a Where()
.
So async
and await
inside a LINQ statement is legal syntax, but does it do nothing at all or does it have a certain use?
The async keyword turns a method into an async method, which allows you to use the await keyword in its body. When the await keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete.
Note that there are no async versions of some LINQ operators such as Where or OrderBy, because these only build up the LINQ expression tree and don't cause the query to be executed in the database. Only operators which cause query execution have async counterparts.
The biggest advantage of using async and await is, it is very simple and the asynchronous method looks very similar to a normal synchronous methods. It does not change programming structure like the old models (APM and EAP) and the resultant asynchronous method look similar to synchronous methods.
async/await is single thread event based model. Which allows you to run code out-of-order until the line of code await. In Raku it would actually wait at the await . sub example { my $p = do-something-async; say 'next line'; await $p; say 'done awaiting'}; sub do-something-async { return Promise.in(5).
I recommend that you not think of this as "using async
within LINQ". Keep in mind what's in-between the two: delegates. Several LINQ operators take delegates, and async
can be used to create an asynchronous delegate.
So, when you have an asynchronous method BazAsync
that returns a Task
:
Task BazAsync(TBar bar);
then this code results in a sequence of tasks:
IEnumerable<Task> tasks = bars.Select(bar => BazAsync(bar));
Similarly, if you use async
and await
within the delegate, you're creating an asynchronous delegate that returns a Task
:
IEnumerable<Task> tasks = bars.Select(async bar => await BazAsync(bar));
These two LINQ expressions are functionally equivalent. There are no important differences.
Just like regular LINQ expressions, the IEnumerable<Task>
is lazy-evaluated. Only, with asynchronous methods like BazAsync
, you usually do not want accidental double-evaluation or anything like that. So, when you project to a sequence of tasks, it's usually a good idea to immediately reify the sequence. This calls BazAsync
for all the elements in the source sequence, starting all the tasks going:
Task[] tasks = bars.Select(bar => BazAsync(bar)).ToArray();
Of course, all we've done with Select
is start an asynchronous operation for each element. If you want to wait for them all to complete, then use Task.WhenAll
:
await Task.WhenAll(tasks);
Most other LINQ operators do not work as cleanly with asynchronous delegates. Select
is pretty straightforward: you're just starting an asynchronous operation for each element.
does it have a certain use
Sure. With async and await inside a LINQ statement you can e.g. do something like this:
var tasks = foos.Select( async foo =>
{
var intermediate = await DoSomethingAsync( foo );
return await DoSomethingElseAsync( intermediate );
} ).ToList();
await Task.WhenAll(tasks);
Without async/await inside a LINQ statement you're not awaiting anything inside the LINQ statement, so you can't process the result, or await for something else.
Without async/await, in the LINQ statement you're only starting tasks, but not waiting for them to complete. They'll still complete eventually, but it'll happen long after the control will leave the LINQ statement, so you can only access their results after the WhenAll
line will complete, but not inside the LINQ statement.
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