Apakah blok iterator biasa (yaitu "yield return") tidak kompatibel dengan "async" dan "await"?
Ini memberi gambaran bagus tentang apa yang saya coba lakukan:
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;
}
Namun, saya mendapatkan kesalahan kompiler yang mengutip "tidak dapat memuat string pesan dari sumber".
Ini upaya lainnya:
async Task<IEnumerable<Foo>> Method(String [] Strs)
{
foreach(var str in strs)
{
yield return await DoSomethingAsync( str)
}
}
Tetapi sekali lagi, kompilator mengembalikan kesalahan: "tidak dapat memuat string pesan dari sumber daya".
Ini adalah kode pemrograman sebenarnya dalam proyek saya
Ini sangat berguna ketika saya memiliki Tugas Daftar, tugas itu bisa mendownload HTML dari URL dan saya menggunakan sintaks "yield return await task", hasilnya saya inginkan IEnumerable<Foo>
. Saya tidak ingin menulis kode ini:
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;
}
Tapi sepertinya aku harus melakukannya.
Terima kasih atas bantuannya.
c#
async-await
c#-5.0
iasyncenumerable
jiangzhen
sumber
sumber
IAsyncEnumerator<T>
tipe seperti yang didefinisikan oleh Arne.Jawaban:
Apa yang Anda gambarkan dapat dicapai dengan
Task.WhenAll
metode ini. Perhatikan bagaimana kode berubah menjadi satu baris sederhana. Apa yang terjadi adalah setiap url individu mulai mengunduh dan kemudianWhenAll
digunakan untuk menggabungkan operasi tersebut menjadi satuTask
yang dapat ditunggu.sumber
async
dari metode dan lakukanreturn Task.WhenAll
secara langsung.urls.Select(DownloadHtmlAsync)
Is it possible to await yield?
Anda baru saja menemukan solusi untuk kasus penggunaan yang satu ini. Tidak menjawab pertanyaan umum.Task<string[]>
karena ini akan menunjukkan bahwa Anda tidak lagi mengembalikan iterator dengan eksekusi yang ditangguhkan yaitu. semua URL sedang diunduh.tl; dr Iterators yang diimplementasikan dengan yield adalah konstruksi pemblokiran, sehingga untuk saat ini await dan yield tidak kompatibel.
Panjang Karena mengulangi
IEnumerable
operasi pemblokiran, memanggil metode yang ditandai sebagaiasync
akan tetap mengeksekusinya dengan cara pemblokiran, karena harus menunggu hingga operasi tersebut selesai.Menunggu
Method
Arti campuran . Apakah Anda ingin menunggu sampaiTask
memilikiIEnumerable
dan kemudian memblokir iterasinya? Atau apakah Anda mencoba menunggu setiap nilaiIEnumerable
?Saya berasumsi yang kedua adalah perilaku yang diinginkan dan dalam hal ini semantik Iterator yang ada tidak akan berfungsi. Itu
IEnumerator<T>
antarmuka pada dasarnya adalahSaya mengabaikan
Reset()
karena tidak masuk akal untuk urutan hasil asinkron. Tetapi yang Anda butuhkan adalah sesuatu seperti ini:Tentu saja,
foreach
juga tidak akan berfungsi dengan ini dan Anda harus mengulang secara manual seperti ini:sumber
foreach
dukungan untuk hipotesis AndaIAsyncEnumerator<T>
?Menurut fitur baru di C # 8.0 ( tautan # 1 dan tautan # 2 ) kami akan memiliki
IAsyncEnumerable<T>
dukungan antarmuka yang memungkinkan Anda untuk mengimplementasikan upaya kedua Anda. Ini akan terlihat seperti ini:Kita dapat mencapai perilaku yang sama di C # 5 tetapi dengan semantik yang berbeda:
Jawaban Brian Gideon menyiratkan bahwa kode panggilan akan mendapatkan sekumpulan hasil yang diperoleh secara paralel secara asinkron. Kode di atas menyiratkan bahwa kode pemanggil akan mendapatkan hasil seperti dari streaming satu per satu secara asynchronous.
sumber
Saya tahu bahwa saya terlambat dengan jawabannya, tetapi berikut adalah solusi sederhana lain yang dapat dicapai dengan pustaka ini:
GitHub: https://github.com/tyrotoxin/AsyncEnumerable
NuGet.org: https: //www.nuget .org / packages / AsyncEnumerator /
Jauh lebih sederhana daripada Rx.
sumber
Fitur ini akan tersedia mulai C # 8.0. https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/
Dari MSDN
Aliran asinkron
Fitur async / await C # 5.0 memungkinkan Anda menggunakan (dan menghasilkan) hasil asinkron dalam kode langsung, tanpa callback:
Tidak terlalu membantu jika Anda ingin menggunakan (atau menghasilkan) aliran hasil yang berkelanjutan, seperti yang mungkin Anda dapatkan dari perangkat IoT atau layanan cloud. Aliran asinkron tersedia untuk itu.
Kami memperkenalkan IAsyncEnumerable, yang persis seperti yang Anda harapkan; versi IEnumerable yang tidak sinkron. Bahasa memungkinkan Anda menunggu untuk masing-masing ini untuk mengkonsumsi elemen mereka, dan menghasilkan kembali kepada mereka untuk menghasilkan elemen.
sumber
Ada rencana yang harus dilakukan
https://github.com/dotnet/csharplang/issues/43
Tapi saat ini tidak memungkinkan
sumber
Pertama-tama, perlu diingat bahwa hal-hal Async belum selesai. Perjalanan tim C # masih panjang sebelum C # 5 dirilis.
Karena itu, saya pikir Anda mungkin ingin mengumpulkan tugas yang dipecat dalam
DownloadAllHtml
fungsi dengan cara yang berbeda.Misalnya, Anda dapat menggunakan sesuatu seperti ini:
Bukan berarti
DownloadAllUrl
fungsinya BUKAN panggilan async. Namun, Anda dapat menerapkan panggilan async pada fungsi lain (yaituDownloadHtmlAsync
).Task Parallel Library memiliki fungsi
.ContinueWhenAny
dan.ContinueWhenAll
.Itu bisa digunakan seperti ini:
sumber
Task<IEnumerable<Task<string>>> DownloadAllUrl
. Atau, jika Anda menginginkan tindakan 'footer'IEnumerable<Task>
. Misalnya gist.github.com/1184435async
hal ini selesai dan C # 5.0 adalah dirilis, ini dapat diperbarui.Sayangnya, hasil tidak bekerja dengan menunggu. Tapi inilah kegunaan Rx. Lihat https://msdn.microsoft.com/library/hh242985
sumber
Solusi ini berfungsi seperti yang diharapkan. Perhatikan
await Task.Run(() => enumerator.MoveNext())
bagiannya.sumber