Exception dalam kode asinkron tidak melompat langsung ke pemanggil. Ketika method asinkron melempar exception, runtime menangkapnya, menyimpannya di dalam objek Task, dan mengubah status task menjadi Faulted. Exception itu baru dilempar ulang ke titik await yang menunggu task tersebut. Pola ini menjelaskan mengapa try/catch di sekitar await cukup untuk menangkap kesalahan yang terjadi jauh di dalam method asinkron lain.
Perjalanan Sebuah Exception #
- Exception di dalam method asinkron ditangkap runtime dan disimpan pada task yang sedang berjalan.
- Status task berubah menjadi
Faulted; sisa badan method tidak dijalankan. awaitmembaca kembali exception yang tersimpan dan melemparnya pada pemanggil, sehingga blokcatchbiasa dapat menangkapnya.- Pada
Task.WhenAll, semua exception tersimpan dalamAggregateExceptionpada task gabungan, tetapiawaitmelempar hanya yang pertama.
Contoh: Menangkap Exception dan Menghitung Sisanya #
Kode berikut menangkap exception pembayaran, lalu menghitung berapa exception yang tercatat ketika dua pengiriman notifikasi gagal bersamaan:
static async Task<int> ProsesPembayaranAsync(int nominal)
{
await Task.Delay(50);
if (nominal <= 0)
{
throw new InvalidOperationException("Nominal pembayaran harus positif");
}
return nominal;
}
static async Task GagalKirimAsync(string penyebab)
{
await Task.Delay(30);
throw new InvalidOperationException(penyebab);
}
// Pemanggil:
Task<int> proses = ProsesPembayaranAsync(-50000);
try
{
int total = await proses;
Console.WriteLine($"Pembayaran berhasil: Rp{total}");
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"Tertangkap saat await: {ex.Message}");
Console.WriteLine($"Status task: {proses.Status}");
}
Task gagal1 = GagalKirimAsync("Koneksi database terputus");
Task gagal2 = GagalKirimAsync("Format data tidak sesuai");
Task gabungan = Task.WhenAll(gagal1, gagal2);
try
{
await gabungan;
}
catch (InvalidOperationException)
{
Console.WriteLine($"Exception tercatat oleh WhenAll: {gabungan.Exception.InnerExceptions.Count}");
}Output yang Dihasilkan:
Tertangkap saat await: Nominal pembayaran harus positif
Status task: Faulted
Exception tercatat oleh WhenAll: 2Mari kita bedah perjalanannya:
ProsesPembayaranAsync(-50000): method berjalan sampai pernyataanthrow; exception disimpan pada task dan sisa method dilewati.catch (InvalidOperationException ex):awaitmelempar ulang exception yang tersimpan, sehingga blok catch pemanggil menangkapnya seperti exception sinkron biasa.proses.Status: statusFaultedterbaca setelah catch, membuktikan kesalahan tersimpan pada task.gabungan.Exception.InnerExceptions.Count:WhenAllmencatat dua exception;awaitmelempar yang pertama, sedangkan keduanya tetap dapat diperiksa melalui propertiException.
Kesalahan sudah tertangani dengan rapi. Bab berikutnya beralih ke bentuk data asinkron: stream yang mengirim item satu per satu seiring waktu.