以下代码段编译,但我希望它等待任务结果,而不是给我一个List<Task<T>>
。
var foo = bars.Select(async bar => await Baz(bar)).ToList()
如指出here,您需要使用Task.WhenAll
:
var tasks = foos.Select(async foo => await DoSomethingAsync(foo)).ToList();
await Task.WhenAll(tasks);
但a comment指出async
内的await
和Select()
不需要:
var tasks = foos.Select(foo => DoSomethingAsync(foo)).ToList();
类似的问题here,有人试图在Where()
内使用异步方法。
因此LINQ语句中的async
和await
是合法的语法,但是它什么都不做或者是否有某种用途?
答案 0 :(得分:30)
我建议您不要将其视为&#34;在LINQ&#34;中使用async
。请记住两者之间的内容:代表们。几个LINQ运算符接受委托,async
可用于创建异步委托。
因此,如果您有一个返回BazAsync
的异步方法Task
:
Task BazAsync(TBar bar);
然后这段代码会产生一系列任务:
IEnumerable<Task> tasks = bars.Select(bar => BazAsync(bar));
同样,如果您在委托中使用async
和await
,则需要创建一个返回Task
的异步委托:
IEnumerable<Task> tasks = bars.Select(async bar => await BazAsync(bar));
这两个LINQ表达式在功能上是等价的。没有重要的区别。
就像常规LINQ表达式一样,IEnumerable<Task>
是惰性求值的。只有像BazAsync
这样的异步方法,你通常不想要意外的双重评估或类似的东西。因此,当您投射到一系列任务时,立即重新确定序列通常是一个好主意。这将为源序列中的所有元素调用BazAsync
,开始执行所有任务:
Task[] tasks = bars.Select(bar => BazAsync(bar)).ToArray();
当然,我们使用Select
所做的就是启动每个元素的异步操作。如果您想等待所有内容完成,请使用Task.WhenAll
:
await Task.WhenAll(tasks);
大多数其他LINQ运算符不能与异步委托一起干净地工作。 Select
非常简单:您只是为每个元素启动异步操作。
答案 1 :(得分:6)
是否有特定用途
不确定。使用async和等待LINQ语句,你可以例如做这样的事情:
var tasks = foos.Select( async foo =>
{
var intermediate = await DoSomethingAsync( foo );
return await DoSomethingElseAsync( intermediate );
} ).ToList();
await Task.WhenAll(tasks);
如果没有在LINQ语句中使用async / await,您就不会在LINQ语句中等待任何内容,因此您无法处理结果,或等待其他内容。
如果没有async / await,在LINQ语句中,您只是启动任务,而不是等待它们完成。它们最终仍然会完成,但是在控件离开LINQ语句之后很久就会发生,所以你只能在WhenAll
行完成之后访问它们的结果,但不能在LINQ语句中访问它们的结果