dartasyncerrorMahir4 mnt baca

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

dart
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:

dart
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.
  • catch umum: 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():

dart
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

dart
// 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');
  }
}
dart
// 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

dart
// BURUK: error hilang tanpa jejak
Future<void> main() async {
  try {
    await ambilData(true);
  } catch (e) {
    // kosong: debugging jadi mimpi buruk
  }
}
dart
// 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.

text
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
}