Masalahnya adalah Anda menggunakan kelas non-generik Task
, yang tidak dimaksudkan untuk membuahkan hasil. Jadi, ketika Anda membuat Task
instance lewat delegasi async:
Task myTask = new Task(async () =>
... delegasi diperlakukan sebagai async void
. Sebuah async void
bukan Task
, itu tidak bisa ditunggu, pengecualiannya tidak dapat ditangani, dan itu adalah sumber dari ribuan pertanyaan yang dibuat oleh programmer yang frustrasi di sini di StackOverflow dan di tempat lain. Solusinya adalah menggunakan Task<TResult>
kelas generik , karena Anda ingin mengembalikan hasil, dan hasilnya adalah yang lain Task
. Jadi, Anda harus membuat Task<Task>
:
Task<Task> myTask = new Task<Task>(async () =>
Sekarang ketika Anda Start
bagian luar Task<Task>
itu akan selesai hampir seketika karena tugasnya hanya untuk menciptakan bagian dalam Task
. Anda kemudian harus menunggu bagian dalam Task
juga. Ini adalah bagaimana hal itu dapat dilakukan:
myTask.Start();
Task myInnerTask = await myTask;
await myInnerTask;
Anda memiliki dua alternatif. Jika Anda tidak membutuhkan referensi eksplisit ke bagian dalam Task
maka Anda bisa menunggu bagian luar Task<Task>
dua kali:
await await myTask;
... atau Anda dapat menggunakan metode ekstensi bawaan Unwrap
yang menggabungkan tugas-tugas luar dan dalam menjadi satu:
await myTask.Unwrap();
Bukaan ini terjadi secara otomatis ketika Anda menggunakan metode yang jauh lebih populer Task.Run
yang menciptakan tugas-tugas panas, sehingga metode Unwrap
ini jarang digunakan saat ini.
Jika Anda memutuskan bahwa delegasi async Anda harus mengembalikan hasil, misalnya a string
, maka Anda harus mendeklarasikan myTask
variabel yang bertipe Task<Task<string>>
.
Catatan: Saya tidak menganjurkan penggunaan Task
konstruktor untuk membuat tugas-tugas dingin. Sebagai praktik yang umumnya disukai, untuk alasan saya tidak benar-benar tahu, tapi mungkin karena jarang digunakan sehingga berpotensi menangkap pengguna / pengelola / pengulas kode yang tidak sadar lainnya karena terkejut.
Saran umum: Hati-hati setiap kali Anda memberikan delegasi async sebagai argumen untuk suatu metode. Metode ini idealnya mengharapkan Func<Task>
argumen (artinya memahami delegasi async), atau setidaknya Func<T>
argumen (artinya setidaknya yang dihasilkan Task
tidak akan diabaikan). Dalam kasus yang disayangkan bahwa metode ini menerima Action
, delegasi Anda akan diperlakukan sebagai async void
. Ini jarang yang Anda inginkan, jika pernah.