Os blocos de iteradores regulares (ou seja, "retorno de rendimento") são incompatíveis com "async" e "await"?
Isso dá uma boa ideia do que estou tentando fazer:
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;
}
No entanto, recebo um erro do compilador citando "não foi possível carregar a string da mensagem dos recursos".
Aqui está outra tentativa:
async Task<IEnumerable<Foo>> Method(String [] Strs)
{
foreach(var str in strs)
{
yield return await DoSomethingAsync( str)
}
}
Mas, novamente, o compilador retorna um erro: "não foi possível carregar a string da mensagem dos recursos".
Aqui está o código de programação real do meu projeto
Isso é muito útil quando eu tenho uma tarefa de lista, essa tarefa pode ser baixar HTML de uma URL e eu uso a sintaxe "yield return await task", o resultado é eu quero IEnumerable<Foo>
. Não quero escrever este código:
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;
}
Mas parece que preciso.
Obrigado por qualquer ajuda.
c#
async-await
c#-5.0
iasyncenumerable
Jiangzhen
fonte
fonte
IAsyncEnumerator<T>
tipo definido por Arne.Respostas:
O que você está descrevendo pode ser realizado com o
Task.WhenAll
método. Observe como o código se transforma em uma linha simples. O que acontece é que cada url individual começa a baixar e entãoWhenAll
é usado para combinar essas operações em uma únicaTask
que pode ser aguardada.fonte
async
do método e façareturn Task.WhenAll
diretamente.urls.Select(DownloadHtmlAsync)
Is it possible to await yield?
Você acabou de encontrar uma solução para este caso de uso. Não respondeu à pergunta geral.Task<string[]>
pois isso indicaria que você não está mais retornando um iterador com execução adiada ie. todos os URLs estão sendo baixados.tl; dr Iteradores implementados com rendimento são uma construção de bloqueio, portanto, a partir de agora, await e rendimento são incompatíveis.
Longo Como iterar sobre uma
IEnumerable
é uma operação de bloqueio, chamar um método marcado comoasync
ainda o executará de maneira bloqueadora, pois terá que aguardar o término da operação.A espera
Method
mistura significados. Você deseja aguardar até queTask
tenha umIEnumerable
e, em seguida, bloquear a iteração sobre ele? Ou você está tentando aguardar cada valor doIEnumerable
?Presumo que o segundo seja o comportamento desejado e, nesse caso, a semântica do Iterator existente não funcionará. A
IEnumerator<T>
interface é basicamenteEstou ignorando,
Reset()
pois não faz sentido para uma sequência de resultados assíncronos. Mas o que você precisa é algo assim:Claro,
foreach
também não funcionará com isso e você terá que iterar manualmente assim:fonte
foreach
suporte para sua hipóteseIAsyncEnumerator<T>
?De acordo com os novos recursos do C # 8.0 ( link # 1 e link # 2 ), teremos
IAsyncEnumerable<T>
suporte de interface que permitirá a implementação de sua segunda tentativa. Isso parecerá assim:Podemos obter o mesmo comportamento em C # 5, mas com uma semântica diferente:
A resposta de Brian Gideon implica que o código de chamada obterá de forma assíncrona uma coleção de resultados obtidos em paralelo. O código acima implica que o código de chamada obterá resultados de um fluxo um por um de maneira assíncrona.
fonte
Sei que estou muito atrasado com a resposta, mas aqui está outra solução simples que pode ser alcançada com esta biblioteca:
GitHub: https://github.com/tyrotoxin/AsyncEnumerable
NuGet.org: https: //www.nuget .org / packages / AsyncEnumerator /
É muito mais simples que Rx.
fonte
Este recurso estará disponível a partir do C # 8.0. https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/
Do MSDN
Streams assíncronos
O recurso async / await do C # 5.0 permite que você consuma (e produza) resultados assíncronos em código direto, sem retornos de chamada:
Não é tão útil se você deseja consumir (ou produzir) fluxos contínuos de resultados, como você pode obter de um dispositivo IoT ou um serviço de nuvem. Os streams assíncronos existem para isso.
Apresentamos IAsyncEnumerable, que é exatamente o que você esperaria; uma versão assíncrona de IEnumerable. A linguagem permite que você aguarde foreach sobre estes para consumir seus elementos e render retorno a eles para produzir elementos.
fonte
Havia um plano para fazer
https://github.com/dotnet/csharplang/issues/43
Mas atualmente não é possível
fonte
Em primeiro lugar, tenha em mente que o material do Async não está concluído. A equipe C # ainda tem um longo caminho a percorrer antes que o C # 5 seja lançado.
Dito isso, acho que você pode querer reunir as tarefas que estão sendo disparadas na
DownloadAllHtml
função de uma maneira diferente.Por exemplo, você pode usar algo assim:
Não que a
DownloadAllUrl
função NÃO seja uma chamada assíncrona. Mas, você pode ter a chamada assíncrona implementada em outra função (ou sejaDownloadHtmlAsync
).A Biblioteca Paralela de Tarefas tem as funções
.ContinueWhenAny
e.ContinueWhenAll
.Isso pode ser usado assim:
fonte
Task<IEnumerable<Task<string>>> DownloadAllUrl
. Ou, se você quiser ações de 'rodapé'IEnumerable<Task>
. Por exemplo, gist.github.com/1184435async
material foi concluído e o C # 5.0 foi lançado, isso pode ser atualizado.O Yield não funciona com o await, infelizmente. Mas é para isso que serve o Rx. Confira https://msdn.microsoft.com/library/hh242985
fonte
Esta solução funciona conforme o esperado. Observe a
await Task.Run(() => enumerator.MoveNext())
parte.fonte