Lección 19 · 19 de 20
Iterables y generadores · Dart
Genere secuencias perezosas en Dart con sync*, yield y yield*: trabaje con series infinitas y calcule solo lo que de verdad se pide.

Una función **sync* devuelve un `Iterable` perezoso: su cuerpo no se ejecuta al llamarla, sino a medida que alguien pide valores, y cada yield** entrega uno y deja la función en pausa hasta el siguiente. invoiceNumbers es un bucle sin fin, y no pasa nada: skip(41).take(3) pide justo los números 42 a 44 y la función no avanza más.
readPrices escribe una línea cada vez que entrega un precio, para ver cuándo trabaja. La cadena readPrices().where(...).map(...) todavía no lee nada: solo describe la consulta, y por eso Query built, nothing read yet sale antes que cualquier reading. Los métodos de Iterable como where, map, skip o take también son perezosos.
Al pedir expensive.first empieza el trabajo: se leen 120, 45 y 990, y al llegar al primero que supera 500 se le suma el IVA con aritmética entera (* 121 ~/ 100) y todo se detiene. Los precios 300 y 1500 no se leen nunca, algo que importa con miles de registros o con un origen de datos costoso. Ojo: si recorre expensive dos veces, la función se ejecuta dos veces; guarde el resultado con toList() si lo va a reutilizar.
yield*** entrega todos los valores de otro iterable. chunks lo usa para ser recursiva: entrega el primer bloque de size elementos y delega el resto en otra llamada a sí misma. Es una forma cómoda de paginar una lista.
Código · main.dart
// sync* returns a lazy Iterable: the body runs only as values are requested.Iterable<String> invoiceNumbers(String prefix) sync* { for (var n = 1;; n++) { yield '$prefix-${n.toString().padLeft(4, '0')}'; // infinite, and that's fine }}Iterable<int> readPrices() sync* { for (final price in [120, 45, 990, 300, 1500]) { print(' reading $price'); yield price; }}// yield* hands over every value of another iterable.Iterable<List<T>> chunks<T>(List<T> items, int size) sync* { if (items.length <= size) { yield items; return; } yield items.sublist(0, size); yield* chunks(items.sublist(size), size);}void main() { print(invoiceNumbers('INV').skip(41).take(3).toList()); final expensive = readPrices().where((p) => p > 500).map((p) => p * 121 ~/ 100); print('Query built, nothing read yet'); print('First expensive with VAT: ${expensive.first}'); // stops at 990 print('Pages: ${chunks(['ana', 'luis', 'eva', 'tom', 'iker'], 2).toList()}');}Salida · dart run main.dart
[INV-0042, INV-0043, INV-0044] Query built, nothing read yet reading 120 reading 45 reading 990 First expensive with VAT: 1197 Pages: [[ana, luis], [eva, tom], [iker]]
Para recordar
sync*devuelve unIterableque se calcula al recorrerlo.yieldentrega un valor;yield*, todos los de otro iterable.- Un generador puede ser infinito si solo se piden unos cuantos valores.
where,map,skipytakeno hacen nada hasta que se recorre el resultado.- Cada recorrido vuelve a ejecutar el generador; use
toList()para guardarlo.
Ejercicio
Escriba un generador infinito fibonacci() con sync* y muestre el primer número de Fibonacci mayor que 1000 con firstWhere.