Generadores e Iteración Asíncrona
JavaScript permite pausar y reanudar la ejecución de funciones mediante Funciones Generadoras (function*), así como procesar flujos continuos de datos asíncronos mediante Iteradores Asíncronos y el bucle for await...of.
1. Funciones Generadoras Síncronas (function* y yield) #
A diferencia de una función normal que se ejecuta de principio a fin, una función generadora puede pausarse en cada instrucción yield y devolver un valor, manteniendo su estado interno hasta que se solicite el siguiente valor con .next():
1function* generadorId() { 2 let id = 1; 3 while (true) { 4 yield `USR-${id}`; 5 id++; 6 } 7} 8 9const gen = generadorId(); 10 11console.log(gen.next().value); // "USR-1" 12console.log(gen.next().value); // "USR-2" 13console.log(gen.next().value); // "USR-3"Salida de consola USR-1 USR-2 USR-3
Delegación con yield* #
Permite delegar la iteración en otro generador o iterable:
1function* grupo1() { 2 yield "A"; 3 yield "B"; 4} 5 6function* grupoCompleto() { 7 yield* grupo1(); // Delega en grupo1 8 yield "C"; 9} 10 11console.log([...grupoCompleto()]);Salida de consola [ 'A', 'B', 'C' ]
2. Iteradores Asíncronos (async function* y for await...of) #
Un generador asíncrono combina promesas con generadores, permitiendo pausar la ejecución mientras espera operaciones asíncronas (await) y emitir valores a lo largo del tiempo con yield.
Se consumen de forma natural mediante el bucle for await...of:
1// Simula la lectura paginada de una API 2async function* obtenerPaginasAPI(totalPaginas) { 3 for (let pagina = 1; pagina <= totalPaginas; pagina++) { 4 // Simula retardo de red de 300ms 5 await new Promise(r => setTimeout(r, 300)); 6 yield { pagina, registros: [`Item_${pagina}A`, `Item_${pagina}B`] }; 7 } 8} 9 10async function procesarPaginacion() { 11 console.log("Iniciando lectura paginada..."); 12 13 // Consumo con for await...of 14 for await (const lote of obtenerPaginasAPI(3)) { 15 console.log(`Recibida página ${lote.pagina}:`, lote.registros); 16 } 17 18 console.log("Todas las páginas procesadas."); 19} 20 21procesarPaginacion();Salida de consola Iniciando lectura paginada... Recibida página 1: [ 'Item_1A', 'Item_1B' ] Recibida página 2: [ 'Item_2A', 'Item_2B' ] Recibida página 3: [ 'Item_3A', 'Item_3B' ] Todas las páginas procesadas.
3. Protocolos de Iteración: Symbol.iterator y Symbol.asyncIterator #
Cualquier objeto puede convertirse en iterable implementando estos símbolos bien conocidos (Well-known symbols):
1const coleccionPersonalizada = { 2 elementos: ["Alfa", "Beta", "Gamma"], 3 [Symbol.iterator]() { 4 let indice = 0; 5 return { 6 next: () => { 7 if (indice < this.elementos.length) { 8 return { value: this.elementos[indice++], done: false }; 9 } 10 return { done: true }; 11 } 12 }; 13 } 14}; 15 16for (const item of coleccionPersonalizada) { 17 console.log(item); 18}Salida de consola Alfa Beta Gamma
Resumen del tema
Conceptos clave #
- Generadores Síncronos (
function*): funciones pausables medianteyieldque devuelven un objeto iterador{ value, done }. yield*: operador de delegación para encadenar generadores e iterables de forma transparente.- Generadores Asíncronos (
async function*): emiten secuencias de promesas pausables conawaityyield. - Bucle
for await (const item of asyncIterable): estructura estándar para consumir streams y paginaciones asíncronas de manera secuencial. - Protocolos de Iteración: personalización de bucles implementando métodos con clave
Symbol.iteratoroSymbol.asyncIterator.
Qué debes recordar #
Usa async function* junto con for await...of para procesar flujos de datos asíncronos y paginaciones de APIs con consumo de memoria eficiente.