是否有可能“等待回报DoSomethingAsync()”

常规的迭代器块(即“yield return”)与“asynchronous”和“等待”不兼容?

这给了我想要做的一个好主意:

async Task<IEnumerable<Foo>> Method(String [] Strs) { // I want to compose the single result to the final result, so I use the SelectMany var finalResult = UrlStrings.SelectMany(link => //i have an Urlstring Collection await UrlString.DownLoadHtmlAsync() //download single result; DownLoadHtmlAsync method will Download the url's html code ); return finalResult; } 

但是,我得到一个编译器错误,引用“无法从资源加载消息string”。

这是另一个尝试:

 async Task<IEnumerable<Foo>> Method(String [] Strs) { foreach(var str in strs) { yield return await DoSomethingAsync( str) } } 

但同样,编译器返回一个错误:“无法从资源加载消息string”。


这是我的项目中真正的编程代码

这是非常有用的,当我有一个列表任务,该任务可以从URL下载HTML,我使用语法“yield return await任务”,结果是我想IEnumerable<Foo> 。 我不想写这个代码:

 async Task<IEnumerable<String>> DownLoadAllURL(String [] Strs) { List<Foo> htmls= new ... foreach(var str in strs) { var html= await DownLoadHtmlAsync( str) htmls.Add(item) } return htmls; } 

但似乎我必须。

谢谢你的帮助。

你所描述的可以用Task.WhenAll方法来完成。 注意代码如何变成一个简单的单行程式。 会发生什么是每个单独的url开始下载,然后使用WhenAll将这些操作组合成一个可以等待的Task

 Task<IEnumerable<string>> DownLoadAllUrls(string[] urls) { return Task.WhenAll(from url in urls select DownloadHtmlAsync(url)); } 

tl; dr迭代器与yield一起实现是一个阻塞构造,因此现在正在等待并且yield是不相容的。

Long由于遍历IEnumerable是一个阻塞操作,调用标记为async的方法仍然会以阻塞的方式执行它,因为它必须等待该操作完成。

 async Task<IEnumerable<Foo>> Method(String [] Strs) { foreach(var str in strs) { yield return await DoSomethingAsync( str) } } 

等待Method混合含义。 你想等到Task有一个IEnumerable ,然后阻止迭代它? 或者你是否试图等待IEnumerable每个值?

我假设第二个是所需的行为,在这种情况下,现有的迭代器语义将无法正常工作。 IEnumerator<T>接口基本上是

 public interface IEnumerator<T> T Current; bool MoveNext(); } 

我忽略了Reset()因为它对于一系列asynchronous结果是没有意义的。 但是你需要的是这样的:

 public interface IAsyncEnumerator<T> T Current; Task<bool> MoveNext(); } 

当然, foreach也不能用这个,你必须像这样手动迭代:

 var moveNext = await asyncEnumerator.MoveNext(); while(moveNext) { // get the value that was fetche asynchronously var v = asyncEnumerator.Current; // do something with that value // suspend current execution context until next value arrives or we are done moveNext = await asyncEnumerator.MoveNext(); } 

我知道我的答案为时已晚,但这里有另一个可以用这个库实现的简单解决scheme:
GitHub: https : //github.com/tyrotoxin/AsyncEnumerable
NuGet.org:https://www.nuget.org/packages/AsyncEnumerator/
这比Rx简单得多。

 using System.Collections.Async; static IAsyncEnumerable<string> ProduceItems(string[] urls) { return new AsyncEnumerable<string>(async yield => { foreach (var url in urls) { var html = await UrlString.DownLoadHtmlAsync(url); await yield.ReturnAsync(html); } }); } static async Task ConsumeItemsAsync(string[] urls) { await ProduceItems(urls).ForEachAsync(async html => { await Console.Out.WriteLineAsync(html); }); } 

首先,请记住,asynchronous的东西没有完成。 在C#5发布之前,C#团队还有很长的路要走。

这就是说,我认为你可能希望以不同的方式收集在DownloadAllHtml函数中被触发的任务。

例如,你可以使用这样的东西:

 IEnumerable<Task<string>> DownloadAllUrl(string[] urls) { foreach(var url in urls) { yield return DownloadHtmlAsync(url); } } async Task<string> DownloadHtmlAsync(url) { // Do your downloading here... } 

不是说DownloadAllUrl函数不是一个asynchronous调用。 但是,您可以在另一个函数(即DownloadHtmlAsync )上实现asynchronous调用。

任务并行库具有.ContinueWhenAny.ContinueWhenAll函数。

可以这样使用:

 var tasks = DownloadAllUrl(...); var tasksArray = tasks.ToArray(); var continuation = Task.Factory.ContinueWhenAll(tasksArray, completedTasks => { completedtask }); continuation.RunSynchronously(); 

有计划要做

https://github.com/dotnet/csharplang/issues/43

但目前不可能

此解决scheme按预期工作。 注意await Task.Run(() => enumerator.MoveNext())部分。

 using (var enumerator = myEnumerable.GetEnumerator()) { while (true) { if (enumerator.Current != null) { //TODO: do something with enumerator.Current } var enumeratorClone = monitorsEnumerator; var hasNext = await Task.Run(() => enumeratorClone.MoveNext()); if (!hasNext) { break; } } } 

不幸的是,收益率不能正常工作。 但这是Rx的目的。 查看https://msdn.microsoft.com/library/hh242985