Await For Mendalam
Mengonsumsi stream dengan await for: break, error, dan pola real-time.
Loop yang sabar menunggu
await for adalah cara paling bersih mengonsumsi stream: ia menunggu tiap nilai tiba, memprosesnya, lalu menunggu lagi, sampai stream selesai. Dibanding .listen() dengan callback, alurnya sekuensial dan mudah diikuti.
Analogi: await for itu seperti penjaga pintu yang memeriksa tiap tamu yang datang satu per satu. .listen() itu seperti memasang bel: tiap ada tamu, bel berbunyi dan kamu lari ke pintu.
Kenapa await for ada?
Karena callback .listen() memecah logika menjadi potongan (onData, onError, onDone) yang sulit dirangkai untuk alur kompleks. await for menyatukannya dalam satu badan loop dengan try-catch-finally biasa.
Bentuk dasar dan kontrol alur
Stream<int> angka() async* {
for (int i = 1; i <= 10; i++) {
await Future.delayed(Duration(milliseconds: 100));
yield i;
}
}
Future<void> main() async {
await for (var n in angka()) {
print('Dapat: $n');
if (n == 3) {
print('Cukup, berhenti!');
break; // keluar loop, stream dibatalkan otomatis
}
}
print('Loop selesai.');
}break di dalam await for otomatis membatalkan langganan stream. continue melewati nilai saat ini. Semua kontrol alur loop biasa berlaku.
Contoh kedua, akumulasi real-time:
Stream<double> sensorSuhu() async* {
var data = [36.5, 36.7, 37.8, 36.6, 38.2, 36.4];
for (var s in data) {
await Future.delayed(Duration(milliseconds: 200));
yield s;
}
}
Future<void> main() async {
int hitung = 0;
double total = 0;
double maks = 0;
await for (var suhu in sensorSuhu()) {
hitung++;
total += suhu;
if (suhu > maks) maks = suhu;
if (suhu > 37.5) {
print('PERINGATAN: demam terdeteksi ($suhu°C)');
}
}
print('Rata-rata: ${(total / hitung).toStringAsFixed(1)}°C');
print('Maksimum: $maks°C');
}Pola klasik stream processing: agregasi berjalan (running aggregate) + deteksi kondisi, semua dalam satu loop yang terbaca.
Error handling di await for
Stream<int> kadangRusak() async* {
yield 1;
yield 2;
throw Exception('sensor mati!');
}
Future<void> main() async {
try {
await for (var n in kadangRusak()) {
print('Proses: $n');
}
} catch (e) {
print('Stream error, tapi nilai 1 dan 2 sudah diproses: $e');
} finally {
print('Bersih-bersih resource.');
}
}try-catch-finally biasa bekerja mengelilingi await for. Error stream ditangkap seperti error biasa. Nilai yang sudah diproses tidak hilang.
await for vs listen: kapan pakai apa
Future<void> denganAwaitFor() async {
// Cocok: alur sekuensial, butuh break/return
await for (var n in Stream.fromIterable([1, 2, 3])) {
if (n == 2) return; // bisa return dari fungsi!
print(n);
}
}
void denganListen() {
// Cocok: fire-and-forget, banyak pendengar independen
var sub = Stream.fromIterable([1, 2, 3]).listen(
(n) => print(n),
onDone: () => print('selesai'),
);
// sub.cancel(); // bisa dibatalkan manual
}| await for | listen | |
|---|---|---|
| Gaya | Sekuensial, blocking loop | Callback, non-blocking |
| break/return | Bisa | Tidak (pakai cancel) |
| Banyak pendengar | Tidak (satu loop) | Bisa (broadcast) |
| Cocok untuk | Pipeline pemrosesan | Event handler UI |
Kesalahan umum
1. await for di fungsi sync
// SALAH
// void main() {
// await for (var x in stream) {}
// }Fungsi harus async (mengembalikan Future).
2. Dua await for pada single-subscription stream
Sama seperti listen ganda: stream single-subscription hanya bisa dikonsumsi sekali. Butuh dua konsumen? Broadcast atau buat stream baru.
3. Lupa bahwa break membatalkan stream
Ini biasanya yang diinginkan, tapi pahami konsekuensinya: generator berhenti, resource dibersihkan. Jika butuh "jeda lalu lanjut", jangan break; pakai flag dan continue.
Kesimpulan
await for = konsumsi stream gaya sekuensial dengan kontrol alur penuh (break/continue/return) dan try-catch biasa. Pilih await for untuk pipeline pemrosesan, .listen() untuk event handler.
Catatan teknis:
await forpada broadcast stream melewatkan nilai yang tiba sebelum loop mulai (tidak ada buffer replay). Untuk "nilai terakhir selalu tersedia", pertimbangkan BehaviorSubject dari rxdart.
Tantangan
Monitor suhu
Buat Stream<double> suhuAcak() async* yang yield 10 suhu acak 35.0-39.0. Konsumsi dengan await for: hitung rata-rata, dan break dengan peringatan jika ada suhu > 38.5.
import 'dart:math';
Stream<double> suhuAcak() async* {
var r = Random();
// TODO: yield 10 suhu 35.0 + r.nextDouble() * 4
}
Future<void> main() async {
// TODO: await for, rata-rata, break jika > 38.5
}Kuis Bab
Uji pemahamanmu: Bab 5: Null Safety & Async
Jawab 5 soal berikut, lalu tekan "Periksa Jawaban".
1.Apa arti deklarasi String? nama; di Dart?
2.Apa nilai y setelah: var x; var y = x ?? 'default';
3.Kapan LateInitializationError terjadi?
4.Fungsi yang ditandai async selalu mengembalikan apa?
5.Apa perbedaan utama Future dan Stream?