What actually happens when using async/await inside a LINQ statement?

asynchronous, c#, c#-5.0, linq

Solution

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.

Problem

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?

Original source

Related problems