async* în Dart

O funcție async* produce un Stream: trimite valori cu yield, în timp, și poate folosi await între ele.

Sintaxă
Stream<Tip> generator() async* {
  await ...;              // poate aștepta
  yield valoare;          // trimite un eveniment în stream
  yield* altStream;       // trimite toate evenimentele altui stream
}

Ce face async*

async* este varianta asincronă a lui sync*: funcția întoarce un Stream, adică o succesiune de valori care sosesc în timp. Fiecare yield trimite un eveniment ascultătorilor, iar între ele funcția poate aștepta cu await (o pauză, un fișier, o cerere).

Stream-ul pornește doar când cineva îl ascultă (await for sau listen). Când funcția se termină, stream-ul se închide. O excepție aruncată în ea ajunge ca eroare la ascultător.

Dacă ascultătorul se oprește (de exemplu cu break într-un await for), generatorul se oprește și el la următorul yield. yield* retrimite toate evenimentele altui stream.

Exemple

Exemplul 1: Numărătoare inversă

Câte un număr la fiecare 200 ms; await for le primește pe măsură ce apar.

Stream<int> numaratoare(int de) async* {
  for (var i = de; i > 0; i--) {
    await Future.delayed(const Duration(milliseconds: 200));
    yield i;
  }
}

Future<void> main() async {
  print('Lansarea rachetei:');
  await for (final n in numaratoare(3)) {
    print('$n...');
  }
  print('Start!');
}

Rezultat

Lansarea rachetei:
3...
2...
1...
Start!

Exemplul 2: Transformarea unui alt stream

Generatorul citește comenzile dintr-un stream și trimite mai departe doar pe cele mari, cu un mesaj.

Stream<double> comenzi() => Stream.fromIterable([45.0, 320.5, 89.9, 1200.0]);

Stream<String> comenziMari(Stream<double> sursa) async* {
  await for (final valoare in sursa) {
    if (valoare >= 300) {
      yield 'Comandă mare: $valoare lei';
    }
  }
}

Future<void> main() async {
  await for (final mesaj in comenziMari(comenzi())) {
    print(mesaj);
  }
}

Rezultat

Comandă mare: 320.5 lei
Comandă mare: 1200.0 lei

Exemplul 3: Erori din generator

După al doilea senzor, generatorul aruncă o excepție; ea ajunge în catch-ul ascultătorului.

Stream<String> citesteSenzori() async* {
  yield 'Senzor 1: 21.5 °C';
  yield 'Senzor 2: 22.0 °C';
  throw StateError('senzorul 3 nu răspunde');
}

Future<void> main() async {
  try {
    await for (final citire in citesteSenzori()) {
      print(citire);
    }
  } on StateError catch (e) {
    print('Oprit: ${e.message}');
  }
}

Rezultat

Senzor 1: 21.5 °C
Senzor 2: 22.0 °C
Oprit: senzorul 3 nu răspunde

Greșeli frecvente

  • Să întorci Future<...> dintr-o funcție async*: tipul trebuie să fie Stream<...>.
  • return valoare; în async*: nu este permis, folosește yield.
  • Să aștepți rezultate fără să asculți stream-ul: generatorul nu pornește până nu apare un ascultător.

Comenzi înrudite

  • await for: Parcurge un Stream element cu element, așteptând fiecare eveniment; se termină când stream-ul se închide.
  • sync* și yield: O funcție sync* este un generator: produce elementele unui Iterable unul câte unul, cu yield, doar când sunt cerute.
  • async și await: async marchează o funcție asincronă (întoarce Future), iar await așteaptă rezultatul unui Future fără să blocheze programul.