DMarlon Studio

Lección 15 · 15 de 20

Streams con async* y await for · Dart

Cree streams en Dart con funciones async* y yield, recórralos con await for y transfórmelos con where y map, con una descarga simulada.

Dart · Streams con async* y await for en DMarlon Studio: el código del ejemplo y su salida en la terminal

Una Future da un solo valor; un `Stream` da una secuencia de valores a lo largo del tiempo: el progreso de una descarga, los mensajes de un chat o las lecturas de un sensor. La forma más sencilla de crear uno es una función **async*: cada yield** emite un evento, y entre uno y otro la función puede hacer await.

download simula la descarga de 600 KB en trozos de 160: espera 20 ms por trozo, suma lo recibido sin pasarse del total y emite el porcentaje. Además, nada se ejecuta hasta que alguien escucha el stream.

`await for` recorre el stream: espera cada evento, ejecuta el cuerpo del bucle y continúa hasta que el stream se cierra, que aquí es cuando la función termina. Después de las cuatro líneas de progreso, main sigue con lo siguiente.

Los streams tienen los mismos métodos que las listas (where, map, fold, toList…), pero asíncronos: toList() devuelve una Future. Stream.fromIterable convierte una lista en stream, y del ejemplo se quedan las lecturas de más de 24 grados. Stream.periodic emite un número cada 10 ms y no termina nunca; un `break` dentro de await for cancela la suscripción y el stream se detiene.

Código · main.dart

// async* returns a Stream: each yield sends one event, over time.Stream<int> download(int totalKb, int chunkKb) async* {  var received = 0;  while (received < totalKb) {    await Future.delayed(const Duration(milliseconds: 20)); // a network chunk    received += chunkKb;    if (received > totalKb) received = totalKb;    yield received * 100 ~/ totalKb;  }}Future<void> main() async {  // await for waits for each event until the stream closes.  await for (final percent in download(600, 160)) {    print('Downloaded $percent%');  }  // Streams have where, map, fold... like lists, but asynchronous.  final readings = Stream.fromIterable([18.5, 21.0, 25.4, 30.1, 27.9]);  final hot = await readings.where((t) => t > 24).map((t) => '$t C').toList();  print('Hot readings: $hot');  // A stream that never ends: break cancels the subscription.  final ticks = Stream.periodic(const Duration(milliseconds: 10), (i) => i + 1);  await for (final tick in ticks) {    if (tick > 3) break;    print('Tick $tick');  }  print('Stopped listening');}

Salida · dart run main.dart

Downloaded 26%
Downloaded 53%
Downloaded 80%
Downloaded 100%
Hot readings: [25.4 C, 30.1 C, 27.9 C]
Tick 1
Tick 2
Tick 3
Stopped listening

Para recordar

  • Un Stream es una secuencia de valores asíncronos.
  • Una función async* crea un stream, y yield emite cada valor.
  • await for espera cada evento hasta que el stream se cierra.
  • where, map y toList también existen para streams.
  • break dentro de await for cancela la suscripción.

Ejercicio

Cree Stream<String> countdown(int from) async* que emita 3, 2, 1 y Go! con 50 ms entre cada valor, y recórralo con await for.