Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
494 views
in Technique[技术] by (71.8m points)

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

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...