Masalahnya adalah Anda menggunakan kelas non-generik Task, yang tidak dimaksudkan untuk membuahkan hasil. Jadi, ketika Anda membuat Taskinstance lewat delegasi async:
Task myTask = new Task(async () =>
... delegasi diperlakukan sebagai async void. Sebuah async voidbukan 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 Startbagian luar Task<Task>itu akan selesai hampir seketika karena tugasnya hanya untuk menciptakan bagian dalam Task. Anda kemudian harus menunggu bagian dalam Taskjuga. 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 Taskmaka Anda bisa menunggu bagian luar Task<Task>dua kali:
await await myTask;
... atau Anda dapat menggunakan metode ekstensi bawaan Unwrapyang 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.Runyang menciptakan tugas-tugas panas, sehingga metode Unwrapini jarang digunakan saat ini.
Jika Anda memutuskan bahwa delegasi async Anda harus mengembalikan hasil, misalnya a string, maka Anda harus mendeklarasikan myTaskvariabel yang bertipe Task<Task<string>>.
Catatan: Saya tidak menganjurkan penggunaan Taskkonstruktor 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 Tasktidak 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.