Sind reguläre Iteratorblöcke (dh "Yield Return") nicht mit "async" und "await" kompatibel?
Dies gibt eine gute Vorstellung davon, was ich versuche:
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;
}
Ich erhalte jedoch einen Compilerfehler mit der Angabe "Nachrichtenzeichenfolge kann nicht aus Ressourcen geladen werden".
Hier ist ein weiterer Versuch:
async Task<IEnumerable<Foo>> Method(String [] Strs)
{
foreach(var str in strs)
{
yield return await DoSomethingAsync( str)
}
}
Der Compiler gibt jedoch erneut den Fehler "Nachrichtenzeichenfolge kann nicht aus Ressourcen geladen werden" zurück.
Hier ist der eigentliche Programmcode in meinem Projekt
Dies ist sehr nützlich, wenn ich eine Listenaufgabe habe, diese Aufgabe HTML von einer URL herunterladen kann und ich die Syntax "Yield Return Wait Task" verwende. Das Ergebnis ist das, was ich will IEnumerable<Foo>
. Ich möchte diesen Code nicht schreiben:
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;
}
Aber es scheint, dass ich muss.
Vielen Dank für jede Hilfe.
quelle
IAsyncEnumerator<T>
von Arne definierten Typ.Antworten:
Was Sie beschreiben, kann mit der
Task.WhenAll
Methode erreicht werden. Beachten Sie, wie sich der Code in einen einfachen Einzeiler verwandelt. Was passiert ist, dass jede einzelne URL mit dem Herunterladen beginnt und dann verwendetWhenAll
wird, um diese Vorgänge zu einer einzigen zu kombinieren, auf die gewartet werdenTask
kann.quelle
async
aus der Methode entfernen undreturn Task.WhenAll
direkt ausführen.urls.Select(DownloadHtmlAsync)
Is it possible to await yield?
Sie gerade eine Problemumgehung für diesen einen Anwendungsfall gefunden haben. Hat die allgemeine Frage nicht beantwortet.Task<string[]>
da dies darauf hinweisen würde, dass Sie keinen Iterator mit verzögerter Ausführung mehr zurückgeben, d. H. Alle URLs werden heruntergeladen.tl; dr Iteratoren, wie sie mit Ausbeute implementiert sind, sind ein blockierendes Konstrukt, so dass ab sofort warten und Ausbeute nicht kompatibel sind.
Long Da das Iterieren über ein
IEnumerable
Blockierungsvorgang ist, wird der Aufruf einer als gekennzeichneten Methodeasync
weiterhin blockierend ausgeführt, da auf den Abschluss dieses Vorgangs gewartet werden muss.Das Warten
Method
mischt Bedeutungen. Möchten Sie warten, bis das einTask
hat,IEnumerable
und dann blockieren, um darüber zu iterieren? Oder versuchen Sie, jeden Wert von zu erwartenIEnumerable
?Ich gehe davon aus, dass das zweite das gewünschte Verhalten ist und in diesem Fall die vorhandene Iterator-Semantik nicht funktioniert. Die
IEnumerator<T>
Schnittstelle ist im GrundeIch ignoriere,
Reset()
da es für eine Folge von asynchronen Ergebnissen keinen Sinn macht. Aber was Sie brauchen würden, ist so etwas:Funktioniert natürlich
foreach
auch nicht damit und Sie müssten manuell wie folgt iterieren:quelle
foreach
Ihre Hypothese unterstütztIAsyncEnumerator<T>
?Entsprechend den neuen Funktionen in C # 8.0 ( Link Nr. 1 und Link Nr. 2 )
IAsyncEnumerable<T>
bieten wir Schnittstellenunterstützung, mit der Sie Ihren zweiten Versuch implementieren können. Es wird so aussehen:Wir können das gleiche Verhalten bei C # 5 erreichen, aber mit einer anderen Semantik:
Die Antwort von Brian Gideon impliziert, dass der aufrufende Code asynchron eine Sammlung von Ergebnissen erhält, die parallel erhalten wurden. Der obige Code impliziert, dass der aufrufende Code Ergebnisse wie von einem Stream nacheinander auf asynchrone Weise erhält.
quelle
Ich weiß, dass ich mit der Antwort zu spät bin, aber hier ist eine weitere einfache Lösung, die mit dieser Bibliothek erreicht werden kann:
GitHub: https://github.com/tyrotoxin/AsyncEnumerable
NuGet.org: https: //www.nuget .org / packages / AsyncEnumerator /
Es ist viel einfacher als Rx.
quelle
Diese Funktion ist ab C # 8.0 verfügbar. https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/
Von MSDN
Asynchrone Streams
Mit der Async / Warten-Funktion von C # 5.0 können Sie asynchrone Ergebnisse in einfachem Code ohne Rückrufe verarbeiten (und erzeugen):
Es ist nicht so hilfreich, wenn Sie kontinuierliche Ergebnisströme nutzen (oder produzieren) möchten, wie Sie sie möglicherweise von einem IoT-Gerät oder einem Cloud-Dienst erhalten. Dafür gibt es asynchrone Streams.
Wir führen IAsyncEnumerable ein, genau das, was Sie erwarten würden. eine asynchrone Version von IEnumerable. Mit der Sprache können Sie darauf warten, dass diese über ihre Elemente verbraucht werden, und sie zurückgeben, um Elemente zu produzieren.
quelle
Es gab einen Plan zu tun
https://github.com/dotnet/csharplang/issues/43
Derzeit ist dies jedoch nicht möglich
quelle
Denken Sie zunächst daran, dass das Async-Zeug noch nicht fertig ist. Das C # -Team hat noch einen langen Weg vor sich, bevor C # 5 veröffentlicht wird.
Abgesehen davon denke ich, dass Sie die Aufgaben, die in der
DownloadAllHtml
Funktion ausgelöst werden, auf andere Weise erfassen möchten .Sie können beispielsweise Folgendes verwenden:
Nicht dass die
DownloadAllUrl
Funktion KEIN asynchroner Aufruf ist. Sie können den asynchronen Aufruf jedoch auch für eine andere Funktion (dhDownloadHtmlAsync
) implementieren lassen .Die Task Parallel Library verfügt über die Funktionen
.ContinueWhenAny
und.ContinueWhenAll
.Das kann so verwendet werden:
quelle
Task<IEnumerable<Task<string>>> DownloadAllUrl
. Oder wenn Sie Fußzeilenaktionen möchtenIEnumerable<Task>
. ZB gist.github.com/1184435async
Material ist fertig und C # 5.0 ist freigegeben, dies kann aktualisiert werden.Ertrag funktioniert leider nicht mit Warten. Aber dafür ist Rx da. Überprüfen Sie https://msdn.microsoft.com/library/hh242985
quelle
Diese Lösung funktioniert wie erwartet. Beachten Sie den
await Task.Run(() => enumerator.MoveNext())
Teil.quelle