Generatoare Asincrone și Transformarea Fluxurilor în Dart: async*, yield*, StreamTransformer și RxDart în Dart

Ghid avansat pentru procesarea fluxurilor de date în Dart: crearea de generatoare asincrone cu async* și yield*, fluxuri Single-Subscription vs Broadcast, construirea de StreamTransformer personalizate și concepte reactive.

Sintaxă
Stream<int> countStream(int max) async* { for (int i = 1; i <= max; i++) yield i; }

În timp ce un Future<T> reprezintă o singură valoare care va deveni disponibilă în viitor, un Stream<T> modelează o secvență asincronă de date sau evenimente emise în timp (rețea, evenimente UI, date din socket-uri sau senzori hardware).

Pentru a produce stream-uri într-un mod declarativ și lizibil, Dart oferă generatoarele asincrone, definite prin sintaxa async* și cuvintele cheie yield (pentru a emite o valoare unică) și yield* (pentru a delega emiterea către un alt stream sau iterable complet).

Generator Asincron cu async* și yield*
import 'dart:async';

// Generator asincron care emite evenimente temporizate:
Stream countdown(int from) async* {
  for (int i = from; i >= 0; i--) {
    await Future.delayed(const Duration(milliseconds: 200));
    yield i; // Emite valoarea curentă în stream
  }
}

// Delegare către un alt stream folosind yield*:
Stream combinedStream() async* {
  yield 'Start cronometru:';
  // yield* consumă stream-ul 'countdown' și îi retransmite toate valorile:
  yield* countdown(3).map((num) => 'T-minus: $num');
  yield 'Lansare reușită!';
}

Future main() async {
  await for (final message in combinedStream()) {
    print(message);
  }
}

Tipuri Fundamentale de Stream-uri în Dart

Tip de Stream Număr Maxim de Abonamente Comportament dacă nu există abonat Cazuri Tipice de Utilizare
Single-Subscription Stream Exact 1 abonat Pune datele în buffer până când ascultătorul se abonează. Descărcare fișiere, citire din fișiere I/O, răspunsuri HTTP.
Broadcast Stream Multipli abonați simultan Evenimentele sunt emise instant; dacă nu există ascultător, se pierd. Evenimente de click UI, actualizări GPS, WebSocket mesagerie live.
Crearea unui StreamTransformer Personalizat
import 'dart:async';

// Transformator care filtrează datele goale și le convertește în majuscule:
final sanitizeTransformer = StreamTransformer.fromHandlers(
  handleData: (data, EventSink sink) {
    final trimmed = data.trim();
    if (trimmed.isNotEmpty) {
      sink.add(trimmed.toUpperCase());
    }
  },
  handleError: (error, stackTrace, sink) {
    sink.add('EROARE INTERCEPTATĂ: $error');
  },
);

void main() {
  final inputController = StreamController();

  inputController.stream
      .transform(sanitizeTransformer)
      .listen((cleanData) => print('Output: $cleanData'));

  inputController.add('  salutare  ');
  inputController.add('   '); // Ignorat de transformator
  inputController.add('dart reactive programming');
}