# Generadores en Dart (sync*y async*)
Los generadores son funciones especiales que permiten producir una secuencia de valores de manera perezosa (lazy). Dart soporta dos tipos: síncronos y asíncronos.
## Generadores Síncronos (`sync*`)
Devuelven un `Iterable`. Generan valores bajo demanda, bloqueando la ejecución solo cuando se pide el siguiente valor.
- Usan `sync*` en el cuerpo.
- Usan `yield` para emitir valores.
```dart
Iterable countTo(int n) sync* {
print('Generador iniciado');
for (var i = 1; i <= n; i++) {
print('Calculando $i...');
yield i; // Pausa aquí y entrega el valor
}
print('Generador terminado');
}
void main() {
// La función NO se ejecuta aquí
final numbers = countTo(3);
print('Inicio del loop');
// Se ejecuta paso a paso aquí
for (final n in numbers) {
print('Recibido: $n');
}
}
// Output:
// Inicio del loop
// Generador iniciado
// Calculando 1...
// Recibido: 1
// Calculando 2...
// Recibido: 2
// ...
```
### `yield*` (Delegación recursiva)
Permite delegar la generación a otro iterable.
```dart
Iterable countDownFrom(int n) sync* {
if (n > 0) {
yield n;
yield* countDownFrom(n - 1); // Llamada recursiva eficiente
}
}
```
## Generadores Asíncronos (`async*`)
Devuelven un `Stream`. Son la forma más fácil de crear Streams personalizados.
- Usan `async*` en el cuerpo.
- Usan `yield` para emitir valores.
- Pueden usar `await`.
```dart
Stream timerStream() async* {
for (var i = 1; i <= 3; i++) {
await Future.delayed(Duration(seconds: 1)); // Espera asíncrona
yield i; // Emite evento
}
}
void main() async {
await for (final tick in timerStream()) {
print('Tick: $tick');
}
}
```
## Casos de Uso
### 1. Paginación de Datos
Puedes crear un iterable que vaya pidiendo páginas a una API a medida que la UI las solicita.
### 2. Transformación de Datos Compleja
Si tienes que procesar una lista grande pero quizás no necesites todos los resultados (ej. buscar el primer primo), un generador es más eficiente que `list.map().where()` porque no crea listas intermedias.
```dart
// Eficiente: Procesa uno por uno
Iterable getBigNumbers(List inputs) sync* {
for (var n in inputs) {
if (n > 1000) yield n;
}
}
```
### 3. Gestión de Estado (BLoC)
En BLoC, los eventos entran y los estados salen. `async*` es perfecto para esto.
```dart
Stream mapEventToState(Event event) async* {
if (event is LoginEvent) {
yield LoadingState();
try {
await auth.login();
yield SuccessState();
} catch (e) {
yield ErrorState(e.toString());
}
}
}
```
## Conclusión
- Usa `sync*` para generar colecciones (`Iterable`) de manera perezosa y eficiente en memoria.
- Usa `async*` para generar flujos de eventos (`Stream`) a lo largo del tiempo.
- `yield` emite un valor.
- `yield*` delega a otro generador.
Este apartado profundiza en los conceptos clave, proporcionando ejemplos prácticos y mejores prácticas para su aplicación en proyectos reales.
Al comprender estos detalles, podrás diseñar soluciones más robustas, mantenibles y escalables en tus aplicaciones Flutter y Dart.