futureasyncdartMenengah4 mnt baca

Future & async/await

Operasi asinkron di Dart: Future, async, await, dan then.

Masalah yang Diselesaikan Future

Bayangkan aplikasi kasir yang setiap menyimpan struk harus menunggu 2 detik ke server. Kalau kodenya berjalan secara blocking (berhenti dan menunggu di tempat), seluruh UI ikut macet selama 2 detik: tombol tidak bisa ditekan, animasi berhenti, dan pengguna mengira aplikasinya hang.

Flutter hanya punya satu thread utama yang bergantian menjalankan kode dan me-render UI lewat event loop. Jadi operasi lambat seperti HTTP request, baca file, atau query database tidak boleh dijalankan secara blocking di thread ini. Solusinya adalah Future: sebuah janji bahwa nilai akan tersedia nanti. Kamu memulai operasinya, event loop bebas mengerjakan hal lain (seperti merespons sentuhan pengguna), dan ketika operasi selesai, kode lanjutannya dijalankan. UI tetap hidup dan responsif.

Singkatnya: Future menjawab pertanyaan "bagaimana caranya menunggu sesuatu yang lambat tanpa membekukan aplikasi?"

Contoh Sederhana: async dan await

dart
Future<String> ambilNama() async {
  await Future.delayed(const Duration(seconds: 2)); // simulasi network
  return 'Budi';
}

void contoh() async {
  print('Mulai');
  final nama = await ambilNama(); // tunggu 2 detik, UI tetap jalan
  print('Halo, $nama');
  print('Selesai');
}

Output: Mulai -> (tunggu 2 detik) -> Halo, Budi -> Selesai.

Kata kunci async menandai fungsi yang berjalan asinkron, dan await berarti "tunggu future ini selesai, lalu lanjutkan dengan nilainya". Tanpa await, variabel nama berisi Future<String>, bukan String. Ini sumber kebingungan paling umum bagi pemula: kenapa hasil print-nya Instance of 'Future<String>'? Karena lupa await.

Contoh Lebih Nyata: Error Handling dan Operasi Paralel

Di dunia nyata, request bisa gagal. Bungkus await dengan try/catch seperti kode sinkron biasa:

dart
Future<void> muatDashboard() async {
  try {
    // Future.wait: jalankan semuanya BERSAMAAN, tunggu sampai semua selesai
    final hasil = await Future.wait([
      ambilProfil(),
      ambilDaftarMenu(),
      ambilPromo(),
    ]);
    final profil = hasil[0] as Profil;
    final menu = hasil[1] as List<Menu>;
    final promo = hasil[2] as List<Promo>;
    tampilkan(profil, menu, promo);
  } on TimeoutException {
    tampilkanPesan('Koneksi lambat, coba lagi.');
  } catch (e) {
    tampilkanPesan('Gagal memuat: $e');
  }
}

Future.wait jauh lebih cepat daripada tiga await berurutan kalau request-nya independen: total waktunya hanya selama request paling lambat, bukan jumlah semuanya. Kalau satu future gagal, Future.wait langsung melempar error tersebut, jadi try/catch wajib ada.

Edge Cases yang Sering Menjebak

Lupa await (fire and forget tidak disengaja). Fungsi tetap jalan di background tapi hasilnya tidak ditunggu. Gejalanya: data "kosong" padahal request terkirim, atau error yang tidak pernah tertangkap. Kalau memang sengaja tidak menunggu, tandai eksplisit dengan unawaited(...) dari dart:async agar maksudmu jelas dan linter tidak protes.

await di dalam loop. Menulis for (final id in ids) { await ambilDetail(id); } membuat 20 request berjalan serial: 20 kali waktu satu request. Kalau tiap request independen, kumpulkan jadi list future lalu Future.wait. Hanya pakai await serial kalau request berikutnya bergantung pada hasil sebelumnya.

Request menggantung selamanya. Network bisa macet tanpa error. Selalu pasang batas waktu: await ambilData().timeout(const Duration(seconds: 10)). Tanpa ini, loading spinner-mu bisa berputar selamanya.

setState setelah await. Saat future selesai, widget-nya bisa saja sudah di-dispose (pengguna pindah halaman). Memanggil setState pada widget yang sudah mati melempar error. Cek mounted dulu: if (!mounted) return;.

Kapan TIDAK Perlu Async

Jangan jadikan semua fungsi async hanya karena terlihat modern. Komputasi cepat dan sinkron (format tanggal, hitung total keranjang, validasi form) tidak butuh Future. Fungsi async tanpa await di dalamnya hanya menambah overhead dan membingungkan pembaca, karena return-nya selalu Future meski isinya nilai biasa. Aturan praktisnya: pakai async hanya saat ada operasi yang benar-benar menunggu I/O (network, file, database, timer).

Best Practices

  • Beri tipe eksplisit: Future<List<Produk>> jauh lebih jelas daripada Future<dynamic> dan membantu compiler menangkap kesalahan lebih awal.
  • Tangani error di tempat yang tepat: try/catch di fungsi pemanggil, atau biarkan error-nya ditangkap FutureBuilder di lapisan UI. Jangan biarkan future gagal tanpa ada yang menangani.
  • Hindari callback hell: kalau .then sudah berlapis lebih dari dua level, refactor ke async/await. Kode yang mudah dibaca adalah kode yang mudah di-debug.

Catatan teknis: Fungsi async SELALU mengembalikan Future, bahkan jika kamu me-return String biasa. Dan ingat: Dart berjalan di satu isolate utama, jadi async bukan berarti "dikerjakan thread lain", melainkan "tidak memblokir event loop selagi menunggu I/O". Untuk komputasi berat yang benar-benar butuh thread lain, pakai Isolate atau compute(), bukan async.

Tantangan

Simulasi loading

Buat fungsi async ambilMenu() yang delay 2 detik lalu return List<String> 3 menu. Panggil dengan await dan print hasilnya.