¿Los bloques de iteradores regulares (es decir, "rendimiento de retorno") son incompatibles con "async" y "await"?
Esto da una buena idea de lo que estoy tratando de hacer:
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;
}
Sin embargo, obtengo un error del compilador que menciona "no se puede cargar la cadena de mensajes desde los recursos".
Aquí hay otro intento:
async Task<IEnumerable<Foo>> Method(String [] Strs)
{
foreach(var str in strs)
{
yield return await DoSomethingAsync( str)
}
}
Pero nuevamente, el compilador devuelve un error: "no se puede cargar la cadena de mensajes desde los recursos".
Aquí está el código de programación real en mi proyecto.
Esto es muy útil cuando tengo una tarea de lista, esa tarea se puede descargar HTML desde una URL y uso la sintaxis "rendimiento, retorno, espera de tarea", el resultado es que quiero IEnumerable<Foo>
. No quiero escribir 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;
}
Pero parece que tengo que hacerlo.
Gracias por cualquier ayuda.
fuente
IAsyncEnumerator<T>
tipo definido por Arne.Respuestas:
Lo que está describiendo se puede lograr con el
Task.WhenAll
método. Observe cómo el código se convierte en una simple frase. Lo que sucede es que cada URL individual comienza a descargarse y luegoWhenAll
se usa para combinar esas operaciones en una únicaTask
que se puede esperar.fuente
async
del método y hágaloreturn Task.WhenAll
directamente.urls.Select(DownloadHtmlAsync)
Is it possible to await yield?
que acaba de encontrar una solución alternativa para este caso de uso. No respondió la pregunta general.Task<string[]>
ya que esto indicaría que ya no está devolviendo un iterador con ejecución diferida, es decir. se están descargando todas las URL.tl; dr Los iteradores implementados con yield son una construcción de bloqueo, por lo que a partir de ahora, await y yield son incompatibles.
Long Debido a que iterar sobre una
IEnumerable
es una operación de bloqueo, llamar a un método marcado comoasync
todavía lo ejecutará de manera de bloqueo, ya que tiene que esperar a que termine esa operación.La espera
Method
mezcla significados. ¿Quieres esperar hasta queTask
tenga unIEnumerable
y luego bloquear la iteración sobre él? ¿O está tratando de esperar cada valor deIEnumerable
?Supongo que el segundo es el comportamiento deseado y, en ese caso, la semántica del iterador existente no funcionará. La
IEnumerator<T>
interfaz es básicamenteLo ignoro
Reset()
porque no tiene sentido para una secuencia de resultados asincrónicos. Pero lo que necesitarías es algo como esto:Por supuesto,
foreach
tampoco funcionará con esto y tendrías que iterar manualmente de esta manera:fuente
foreach
soporte para tu hipotéticaIAsyncEnumerator<T>
?De acuerdo con las nuevas características en C # 8.0 ( enlace # 1 y enlace # 2 ) tendremos
IAsyncEnumerable<T>
soporte de interfaz que permitirá implementar su segundo intento. Se verá así:Podemos lograr el mismo comportamiento en C # 5 pero con una semántica diferente:
La respuesta de Brian Gideon implica que el código de llamada obtendrá de forma asincrónica una colección de resultados que se obtuvieron en paralelo. El código anterior implica que el código de llamada obtendrá resultados como de una secuencia uno por uno de manera asincrónica.
fuente
Sé que llego demasiado tarde con la respuesta, pero aquí hay otra solución simple que se puede lograr con esta biblioteca:
GitHub: https://github.com/tyrotoxin/AsyncEnumerable
NuGet.org: https: //www.nuget .org / packages / AsyncEnumerator /
Es mucho más simple que Rx.
fuente
Esta función estará disponible a partir de C # 8.0. https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/
De MSDN
Secuencias asincrónicas
La característica async / await de C # 5.0 le permite consumir (y producir) resultados asincrónicos en código sencillo, sin devoluciones de llamada:
No es tan útil si desea consumir (o producir) flujos continuos de resultados, como los que podría obtener de un dispositivo IoT o un servicio en la nube. Las transmisiones asíncronas están ahí para eso.
Presentamos IAsyncEnumerable, que es exactamente lo que esperaría; una versión asincrónica de IEnumerable. El lenguaje te permite esperar a que cada uno de estos consuma sus elementos y les dé retorno para producir elementos.
fuente
Había un plan que hacer
https://github.com/dotnet/csharplang/issues/43
Pero actualmente no es posible
fuente
En primer lugar, tenga en cuenta que las cosas de Async no están terminadas. El equipo de C # todavía tiene un largo camino por recorrer antes de que se lance C # 5.
Dicho esto, creo que es posible que desee recopilar las tareas que se están ejecutando en la
DownloadAllHtml
función de una manera diferente.Por ejemplo, puede usar algo como esto:
No es que la
DownloadAllUrl
función NO sea una llamada asíncrona. Pero, puede implementar la llamada asíncrona en otra función (es decirDownloadHtmlAsync
).La biblioteca de tareas paralelas tiene las funciones
.ContinueWhenAny
y.ContinueWhenAll
.Eso se puede usar así:
fuente
Task<IEnumerable<Task<string>>> DownloadAllUrl
. O, si desea acciones de 'pie de página'IEnumerable<Task>
. Por ejemplo, gist.github.com/1184435async
todo está terminado y se lanza C # 5.0 , esto se puede actualizar.Lamentablemente, Yield no funciona con await. Pero para eso está Rx. Consulte https://msdn.microsoft.com/library/hh242985
fuente
Esta solución funciona como se esperaba. Tenga en cuenta la
await Task.Run(() => enumerator.MoveNext())
parte.fuente