dartasyncfutureMahir4 mnt baca

Future Chaining & Kombinasi

Merangkai Future: then, Future.wait, Future.any, error handling rantai.

Satu Future jarang sendirian

Aplikasi nyata merangkai banyak operasi async: login → ambil profil → ambil daftar → tampilkan. Future chaining mengatur urutan dan kombinasi Future tanpa callback hell.

Analogi: merangkai Future itu seperti merangkai gerbong kereta. Tiap gerbong (Future) punya muatan, rangkaiannya menentukan urutan tiba, dan ada gerbong cadangan jika ada yang rusak.

then chaining: sekuens transformasi

dart
Future<int> ambilIdPengguna() async {
  await Future.delayed(Duration(milliseconds: 200));
  return 42;
}

Future<String> ambilNama(int id) async {
  await Future.delayed(Duration(milliseconds: 200));
  return 'Pengguna-$id';
}

void main() {
  ambilIdPengguna()
      .then((id) => ambilNama(id))
      .then((nama) => nama.toUpperCase())
      .then((namaBesar) => print('Halo, $namaBesar!'))
      .catchError((e) => print('Gagal: $e'));
  // Halo, PENGGUNA-42!
}

Tiap .then() menerima hasil sebelumnya dan mengembalikan Future baru. Satu .catchError() di ujung menangkap error dari seluruh rantai. Bersih, tanpa nesting.

Bandingkan dengan async/await yang ekuivalen:

dart
Future<void> main() async {
  try {
    var id = await ambilIdPengguna();
    var nama = await ambilNama(id);
    print('Halo, ${nama.toUpperCase()}!');
  } catch (e) {
    print('Gagal: $e');
  }
}

Keduanya valid. Rantai then cocok untuk pipeline transformasi; async/await untuk logika bercabang.

Future.wait: paralel

dart
Future<String> req(String nama, int ms) async {
  await Future.delayed(Duration(milliseconds: ms));
  return nama;
}

Future<void> main() async {
  var mulai = DateTime.now();

  // Paralel: semua jalan bersamaan
  var hasil = await Future.wait([
    req('A', 500),
    req('B', 500),
    req('C', 500),
  ]);

  var detik = DateTime.now().difference(mulai).inMilliseconds;
  print('$hasil dalam ${detik}ms'); // [A, B, C] dalam ~500ms, bukan 1500!
}

Future.wait menunggu SEMUA selesai dan mengembalikan list hasil sesuai urutan. Jika satu gagal, langsung error (kecuali pakai eagerError: false).

Future.any dan timeout

dart
Future<String> serverUtama() async {
  await Future.delayed(Duration(seconds: 3));
  return 'utama';
}

Future<String> serverCadangan() async {
  await Future.delayed(Duration(seconds: 1));
  return 'cadangan';
}

Future<void> main() async {
  // Ambil yang tercepat
  var tercepat = await Future.any([serverUtama(), serverCadangan()]);
  print('Menang: $tercepat'); // cadangan

  // Timeout dengan fallback
  var hasil = await serverUtama()
      .timeout(Duration(seconds: 2), onTimeout: () => 'fallback');
  print(hasil); // fallback
}

Future.any = lomba, pemenang diambil. .timeout(onTimeout:) = batas waktu dengan nilai cadangan, pola penting untuk UX (jangan biarkan pengguna menunggu selamanya).

Error di tengah rantai

dart
Future<int> langkah1() async => 10;
Future<int> langkah2(int x) async => throw Exception('rusak di 2');
Future<int> langkah3(int x) async => x * 2;

Future<void> main() async {
  var hasil = await langkah1()
      .then((a) => langkah2(a))
      .then((b) => langkah3(b))
      .catchError((e) {
    print('Rantai gagal: $e');
    return -1; // nilai pemulihan, rantai "selamat"
  });
  print('Hasil akhir: $hasil'); // -1
}

.catchError() bisa mengembalikan nilai pemulihan sehingga rantai lanjut dengan nilai itu. Tanpa catch, error merambat ke pemanggil.

Kesalahan umum

1. Lupa return di dalam then

dart
void main() {
  Future.value(10).then((n) {
    n * 2; // hasil dibuang!
  }).then((x) => print(x)); // null
}
dart
// BENAR
void main() {
  Future.value(10)
      .then((n) => n * 2)
      .then((x) => print(x)); // 20
}

2. Future.wait untuk operasi yang BERGANTUNGAN

Jika B butuh hasil A, jangan paralel! Future.wait untuk independen saja. Bergantungan → rantai then atau await berurutan.

3. catchError menelan tanpa sadar

.catchError((e) => print(e)) mengembalikan null dan rantai dianggap sukses. Jika ingin error tetap merambat setelah logging, tambahkan throw e; di akhir callback.

Kesimpulan

Rantai then untuk pipeline sekuens, Future.wait untuk paralel independen, Future.any untuk lomba, .timeout dengan fallback untuk UX. Satu catchError di ujung menjaga seluruh rantai.

Catatan teknis: Future.wait dengan eagerError: true (default) langsung error saat satu Future gagal. Dengan false, ia menunggu semua selesai baru melempar error pertama. Pilih sesuai kebutuhan.

Tantangan

Lomba server

Buat 3 fungsi server(String nama, int ms) yang delay lalu return nama. Pakai Future.any untuk mengambil pemenang, dan Future.wait untuk mengambil semua. Cetak keduanya dengan waktunya.

text
Future<String> server(String nama, int ms) async {
  await Future.delayed(Duration(milliseconds: ms));
  return nama;
}

Future<void> main() async {
  // TODO: Future.any([server('A', 300), server('B', 100), server('C', 200)])
  // TODO: Future.wait([...])
}