Error Handling di Async
try-catch-finally untuk kode async dan pola penanganan error.
Menangkap error di dunia async
Error di kode async punya perilaku licik: ia tidak langsung meledak di tempat, melainkan "menempel" pada Future dan meledak nanti saat Future dibaca. try-catch dengan async/await adalah cara natural menanganinya, jauh lebih enak daripada rantai .catchError().
Analogi: memesan makanan online. Kalau restoran tutup, kamu tidak tahu saat memesan (Future dibuat), tapi tahu saat pesanan "tiba" (Future dibaca). try-catch adalah caramu menyiapkan rencana cadangan untuk skenario itu.
Kenapa error async spesial?
Karena tanpa penanganan, error async menjadi unhandled dan aplikasimu crash di momen tak terduga, jauh dari kode penyebabnya. Stack trace-nya pun membingungkan. Menangani error async sama pentingnya dengan menulis logika suksesnya.
try-catch dengan await
Future<String> ambilData(bool gagal) async {
await Future.delayed(Duration(seconds: 1));
if (gagal) throw Exception('Jaringan putus!');
return 'Data OK';
}
Future<void> main() async {
try {
var data = await ambilData(true);
print('Sukses: $data');
} catch (e) {
print('Gagal mengambil data: $e');
}
print('Program tetap jalan.');
}Terlihat seperti kode sinkron biasa, tapi menangani error async. await "membuka" Future: jika Future-nya error, error dilempar di titik await dan ditangkap catch.
Contoh kedua, catch spesifik dan finally:
Future<void> proses() async {
try {
var data = await ambilData(true);
print(data);
} on Exception catch (e) {
print('Error jaringan: $e');
} catch (e) {
print('Error tak terduga: $e');
} finally {
print('Bersih-bersih: tutup loading spinner');
}
}
Future<void> main() async {
await proses();
}on Exception catch: hanya menangkap tipe tertentu.catchumum: jaring pengaman terakhir.finally: SELALU jalan, sukses maupun gagal. Tempat ideal menutup loading, koneksi, atau resource.
Timeout: jangan menunggu selamanya
Request macet tanpa timeout = aplikasi hang. Future punya .timeout():
Future<String> lambat() async {
await Future.delayed(Duration(seconds: 5));
return 'Akhirnya!';
}
Future<void> main() async {
try {
var hasil = await lambat().timeout(Duration(seconds: 2));
print(hasil);
} on TimeoutException catch (e) {
print('Terlalu lama! $e');
}
}Setelah 2 detik, TimeoutException dilempar otomatis. Selalu pasang timeout untuk operasi network di aplikasi nyata.
Kesalahan umum
1. try-catch tidak menangkap karena lupa await
// SALAH: error terjadi nanti, di luar try
Future<void> main() async {
try {
ambilData(true); // tanpa await: Future error tidak tertangkap!
} catch (e) {
print('tidak akan pernah tercetak');
}
}// BENAR
Future<void> main() async {
try {
await ambilData(true);
} catch (e) {
print('Tertangkap: $e');
}
}Tanpa await, Future error "lepas" dari blok try. Aturan: selalu await Future yang error-nya ingin ditangkap.
2. Menelan error diam-diam
// BURUK: error hilang tanpa jejak
Future<void> main() async {
try {
await ambilData(true);
} catch (e) {
// kosong: debugging jadi mimpi buruk
}
}// BENAR: minimal catat
Future<void> main() async {
try {
await ambilData(true);
} catch (e) {
print('Error: $e'); // atau tampilkan pesan ke pengguna
}
}3. catchError dan try-catch dicampur sembarangan
Pilih satu gaya per alur: rantai .then()/.catchError() untuk gaya fungsional, try/catch + await untuk gaya imperatif. Mencampur keduanya dalam satu fungsi membuat alur error sulit diikuti.
Kesimpulan
Error async ditangkap dengan try/catch + await (jangan lupa await-nya!), on Tipe untuk spesifik, finally untuk bersih-bersih, dan .timeout() agar tidak menunggu selamanya. Error yang ditangani = aplikasi yang tetap hidup.
Catatan teknis: Error yang dilempar di dalam
async*generator atau listener Stream tidak bisa ditangkap try-catch biasa; ia mengalir ke stream sebagai error event. Penanganan error Stream dibahas di modul stream.
Tantangan
Unduhan dengan retry
Buat fungsi unduh() yang melempar Exception 2x pertama lalu sukses di percobaan ke-3. Bungkus dengan loop retry 3x memakai try-catch, cetak tiap kegagalan.
int percobaan = 0;
Future<String> unduh() async {
percobaan++;
await Future.delayed(Duration(milliseconds: 300));
if (percobaan < 3) throw Exception('Gagal percobaan $percobaan');
return 'Berhasil!';
}
Future<void> main() async {
// TODO: retry 3x
}