Iteradores Asíncronos: for await...of y Async Iteration Protocol
Aprende a iterar sobre datos asíncronos usando for await...of, async iterators y async generators para manejar flujos de datos asíncronos de forma elegante.
TL;DR - Resumen rápido
- for await...of permite iterar sobre iterables asíncronos
- Los iteradores asíncronos implementan Symbol.asyncIterator
- async function* crea generadores asíncronos con await y yield
- Los valores se resuelven uno por uno, no todos a la vez
- Ideal para procesar streams, paginación y datos en tiempo real
Introducción a los Iteradores Asíncronos
Los iteradores asíncronos son una extensión del protocolo de iteración que permite trabajar con valores que se resuelven de forma asíncrona. A diferencia de los iteradores síncronos que producen valores inmediatamente, los iteradores asíncronos producen promesas que se resuelven en el tiempo.
Esto es especialmente útil cuando trabajas con datos que llegan de forma asíncrona: API calls, streams de datos, paginación, o cualquier fuente de datos que requiere tiempo para producir valores. for await...of simplifica enormemente este patrón.
¿Por qué usar iteradores asíncronos?
Los iteradores asíncronos permiten procesar datos asíncronos secuencialmente sin crear callbacks complejos o anidar promesas. Son la base de características modernas como streams en Node.js y procesamiento de datos en tiempo real en el navegador.
for await...of
for await...of es la sintaxis para iterar sobre iterables asíncronos. Funciona de forma similar a for...of, pero espera que cada valor se resuelva antes de continuar con el siguiente. Cada iteración espera a que la promesa se resuelva.
Este ejemplo muestra cómo usar for await...of para iterar sobre un array de promesas. Cada valor se espera y procesa secuencialmente, uno después del otro. Esto es diferente de Promise.all() que procesa todas las promesas en paralelo.
Las características principales de for await...of son:
- Solo funciona dentro de funciones marcadas como async
- Espera cada valor antes de continuar con el siguiente
- Los valores se procesan secuencialmente, no en paralelo
- Funciona con cualquier iterable asíncrono
- Maneja errores automáticamente con try/catch
Iteradores Asíncronos
Los iteradores asíncronos son objetos que implementan el protocolo de iteración asíncrona. En lugar de Symbol.iterator, implementan Symbol.asyncIterator. El método next() retorna una promesa que se resuelve con el valor y el estado done.
Protocolo de Iteración Asíncrona
El protocolo de iteración asíncrona es similar al protocolo de iteración síncrona, pero con una diferencia clave: el método next() retorna una promesa en lugar del valor directamente. Esta promesa se resuelve con el objeto { value, done }.
Este ejemplo muestra cómo crear un iterador asíncrono manual. El método next() retorna una promesa que se resuelve después de un delay simulado. for await...of espera cada promesa antes de continuar.
Diferencia con iteradores síncronos
Los iteradores asíncronos usan Symbol.asyncIterator en lugar de Symbol.iterator, y su método next() retorna una promesa. Esto permite que los valores se produzcan de forma asíncrona mientras se mantiene la sintaxis simple de iteración.
Generadores Asíncronos
Los generadores asíncronos combinan la sintaxis de generadores con async/await. Se definen con async function* y pueden usar await dentro del cuerpo del generador. Esto simplifica enormemente la creación de iteradores asíncronos.
async function* y yield
async function* es la sintaxis para crear generadores asíncronos. Dentro del generador, puedes usar await para esperar operaciones asíncronas y yield para producir valores. El generador automáticamente implementa el protocolo de iteración asíncrona.
Este ejemplo muestra un generador asíncrono que produce valores con delays. Cada yield espera a que la operación asíncrona se complete antes de producir el siguiente valor. for await...of maneja todo esto automáticamente.
Generadores asíncronos vs iteradores manuales
Los generadores asíncronos son mucho más simples que crear iteradores asíncronos manuales. No necesitas implementar Symbol.asyncIterator ni manejar promesas manualmente. async function* hace todo esto automáticamente.
Casos de Uso Prácticos
Los iteradores asíncronos son útiles en muchos escenarios reales donde los datos llegan de forma asíncrona. Desde procesar streams hasta manejar paginación de APIs, los iteradores asíncronos ofrecen soluciones elegantes.
Este ejemplo muestra cómo usar iteradores asíncronos para paginación de API. El generador produce cada página de resultados a medida que se solicita, implementando lazy evaluation para no cargar todos los datos a la vez.
Este ejemplo muestra cómo procesar un stream de datos chunk por chunk. Los iteradores asíncronos son perfectos para streams porque procesan los datos a medida que llegan, sin esperar a que todo el stream se complete.
Lazy evaluation y eficiencia
Los iteradores asíncronos implementan lazy evaluation: los datos se procesan a medida que llegan, no todos a la vez. Esto es especialmente eficiente para streams grandes o paginación, ya que reduces el uso de memoria.
Errores Comunes
Al trabajar con iteradores asíncronos, hay dos errores principales: usar for await...of fuera de una función async, y usar for await...of con un iterable que no implementa Symbol.asyncIterator. El primero lanza un SyntaxError, el segundo un TypeError.
El ejemplo muestra ambos errores y sus soluciones: usar async en la función contenedora para for await...of, y convertir iterables síncronos a asíncronos cuando sea necesario usando generadores asíncronos.
Error común: Olvidar async en la función
for await...of solo funciona dentro de funciones marcadas como async. Si olvidas la palabra clave async, JavaScript lanzará un SyntaxError. Siempre verifica que la función contenedora sea async.
Resumen: Iteradores Asíncronos
Conceptos principales:
- •for await...of permite iterar sobre iterables asíncronos
- •Los iteradores asíncronos implementan Symbol.asyncIterator
- •async function* crea generadores asíncronos automáticamente
- •Los valores se resuelven secuencialmente, uno por uno
- •Los iteradores asíncronos implementan lazy evaluation
Mejores prácticas:
- •Usa for await...of dentro de funciones async
- •Usa async function* para crear iteradores asíncronos simples
- •Usa iteradores asíncronos para paginación de APIs
- •Usa iteradores asíncronos para procesar streams de datos
- •Maneja errores con try/catch en for await...of