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
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:
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
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
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
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
void main() {
Future.value(10).then((n) {
n * 2; // hasil dibuang!
}).then((x) => print(x)); // null
}// 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.waitdenganeagerError: true(default) langsung error saat satu Future gagal. Denganfalse, 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.
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([...])
}