L62 - Generadores e Iteradores
🧠 Concepto
Section titled “🧠 Concepto”Los generadores son funciones especiales que pueden pausar su ejecución y reanudarla después, manteniendo su estado entre pausas. Se declaran con function* y usan yield para devolver valores parcialmente.
Un iterador es un objeto que sabe cómo acceder a los elementos de una colección uno a uno, mediante el método .next().
💻 Función generadora básica
Section titled “💻 Función generadora básica”function* contador() { yield 1; yield 2; yield 3;}
const iterador = contador();
console.log(iterador.next()); // { value: 1, done: false }console.log(iterador.next()); // { value: 2, done: false }console.log(iterador.next()); // { value: 3, done: false }console.log(iterador.next()); // { value: undefined, done: true }Cada llamada a .next():
- value: el valor yield-ado.
- done:
falsesi quedan más yields,truesi terminó.
📝 yield y el flujo
Section titled “📝 yield y el flujo”function* pasos() { console.log('Paso 1: iniciar'); yield '🚀 Listo para paso 2';
console.log('Paso 2: procesar'); yield '⚙️ Listo para paso 3';
console.log('Paso 3: finalizar'); return '✅ Completado';}
const gen = pasos();
console.log(gen.next().value); // '🚀 Listo para paso 2'console.log(gen.next().value); // '⚙️ Listo para paso 3'console.log(gen.next().value); // '✅ Completado'💻 Iterables vs Iteradores
Section titled “💻 Iterables vs Iteradores”Iterable
Section titled “Iterable”Un objeto que implementa Symbol.iterator y puede usarse con for...of.
Iterador
Section titled “Iterador”Un objeto con un método .next() que devuelve { value, done }.
const iterable = [1, 2, 3]; // Los arrays son iterablesconst iterador = iterable[Symbol.iterator](); // Obtenemos el iterador
console.log(iterador.next()); // { value: 1, done: false }console.log(iterador.next()); // { value: 2, done: false }console.log(iterador.next()); // { value: 3, done: false }console.log(iterador.next()); // { value: undefined, done: true }🎯 for…of con generadores
Section titled “🎯 for…of con generadores”function* numerosPares(limite) { for (let i = 0; i <= limite; i += 2) { yield i; }}
for (const num of numerosPares(10)) { console.log(num); // 0, 2, 4, 6, 8, 10}Los generadores son iterables (tienen Symbol.iterator), por eso funcionan con for...of.
📝 Delegación con yield*
Section titled “📝 Delegación con yield*”yield* delega la iteración a otro generador o iterable:
function* subNumeros() { yield 1; yield 2;}
function* principal() { yield 'Inicio'; yield* subNumeros(); // Delega a subNumeros yield* [3, 4, 5]; // Delega a un array yield 'Fin';}
console.log([...principal()]);// ['Inicio', 1, 2, 3, 4, 5, 'Fin']💻 Caso práctico: secuencia infinita
Section titled “💻 Caso práctico: secuencia infinita”function* secuenciaInfinita() { let i = 0; while (true) { yield i++; }}
const numeros = secuenciaInfinita();console.log(numeros.next().value); // 0console.log(numeros.next().value); // 1console.log(numeros.next().value); // 2// No hay límite, pero solo generamos lo que necesitamosVentaja: lazy evaluation
Section titled “Ventaja: lazy evaluation”Los generadores son perezosos (lazy): solo calculan el siguiente valor cuando se les pide. La secuencia infinita no ocupa memoria infinita porque solo existe en el momento de yield.
🎯 Caso práctico: paginación perezosa
Section titled “🎯 Caso práctico: paginación perezosa”async function* paginador(urlBase) { let pagina = 1; let tieneMas = true;
while (tieneMas) { const url = `${urlBase}?page=${pagina}&limit=10`; const res = await fetch(url); const datos = await res.json();
if (!datos.length) { tieneMas = false; } else { yield datos; pagina++; } }}
// Consumir bajo demandafor await (const pagina of paginador('/api/usuarios')) { console.log(`Página con ${pagina.length} usuarios`); // Procesar página... // Podemos hacer break cuando queramos}💻 Comunicación bidireccional
Section titled “💻 Comunicación bidireccional”.next(valor) también puede enviar valores al generador:
function* calculadora() { const a = yield 'Dame el primer número'; const b = yield 'Dame el segundo número'; yield `Resultado: ${a + b}`;}
const calc = calculadora();console.log(calc.next().value); // 'Dame el primer número'console.log(calc.next(5).value); // 'Dame el segundo número'console.log(calc.next(3).value); // 'Resultado: 8'📝 Crear un iterable personalizado
Section titled “📝 Crear un iterable personalizado”const rango = { from: 1, to: 5,
[Symbol.iterator]() { let current = this.from; const last = this.to;
return { next() { if (current <= last) { return { value: current++, done: false }; } return { value: undefined, done: true }; } }; }};
for (const num of rango) { console.log(num); // 1, 2, 3, 4, 5}Con un generador es más conciso:
const rango = { from: 1, to: 5,
*[Symbol.iterator]() { for (let i = this.from; i <= this.to; i++) { yield i; } }};🎯 Regla de oro: usa generadores cuando necesites secuencias perezosas (grandes o infinitas), flujos controlados (paginación, streams) o comunicación bidireccional con una función.