Async/await en forEach: cómo armonizar la asincronía con los bucles

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Por qué tu async/await se calla en forEach? Analicémoslo de un vistazo

\n\n

¿Alguna vez has escrito algo como este código, esperando que ejecutara operaciones asíncronas en orden?

\n\n
const users = ['alice', 'bob', 'charlie'];\n\nusers.forEach(async (user) => {\n  const data = await fetch(`https://api.example.com/users/${user}`);\n  console.log(await data.json());\n});\n\nconsole.log('¡Listo!');
\n\n

¿Y obtenías "¡Listo!" antes de que se cargaran los datos? ¿Te suena? No estás solo. Esta es una de las preguntas más populares en Stack Overflow con miles de visitas. El problema es que forEach es un método síncrono. Simplemente ejecuta los callbacks, pero no espera a que terminen. Tus funciones async se inician, pero forEach ya sigue adelante, como un velocista al que no le importa la meta de los demás.

\n\n

En este artículo no solo mostraremos cómo solucionarlo. Analizaremos por qué sucede y te daremos tres soluciones funcionales con ejemplos. Aprenderás cuándo usar Promise.all y cuándo usar el clásico for...of. ¡Allá vamos!

\n\n

Anatomía del problema: forEach y las promesas

\n\n

Miremos bajo el capó. El método forEach en JavaScript está diseñado así:

\n\n
Array.prototype.forEach = function(callback) {\n  for (let i = 0; i < this.length; i++) {\n    callback(this[i], i, this);\n  }\n};
\n\n

¿Ves? Simplemente llama a callback para cada elemento, pero no devuelve nada. Incluso si el callback devuelve una Promise, forEach la ignora. No espera, no recoge resultados — solo la ejecuta y la olvida.

\n\n

Cuando escribes async (user) => { ... }, tu función devuelve una Promise. Pero forEach no sabe qué hacer con ella. Como resultado, todas las peticiones se inician en paralelo (si es fetch), pero no puedes esperar a que terminen. El código después de forEach se ejecuta instantáneamente.

\n\n

Ejemplo real: cómo se ve en la práctica

\n\n

Imagina que procesas un array de IDs de productos y para cada uno necesitas obtener el precio de la base de datos:

\n\n
const productIds = [101, 102, 103];\n\nasync function getPrice(id) {\n  // Simulación de una consulta a la BD\n  return new Promise(resolve => {\n    setTimeout(() => {\n      resolve(`Precio para ${id}: $${Math.floor(Math.random() * 100)}`);\n    }, 1000);\n  });\n}\n\nproductIds.forEach(async (id) => {\n  const price = await getPrice(id);\n  console.log(price);\n});\n\nconsole.log('¡Todos los precios cargados!'); // Se ejecuta inmediatamente, no después de 3 segundos
\n\n

Resultado en la consola:

\n
¡Todos los precios cargados!\nPrecio para 101: $42\nPrecio para 102: $78\nPrecio para 103: $15
\n\n

¿Estás de acuerdo? No es lo que esperábamos. La buena noticia: hay soluciones simples y elegantes.

\n\n

Solución n.º 1: for...of — ejecución secuencial

\n\n

La forma más sencilla de hacer que async/await funcione en un bucle es usar for...of. A diferencia de forEach, for...of funciona con async/await de forma nativa, porque tú controlas el flujo de ejecución dentro del bloque.

\n\n
const productIds = [101, 102, 103];\n\nasync function getPrice(id) {\n  return new Promise(resolve => {\n    setTimeout(() => {\n      resolve(`Precio para ${id}: $${Math.floor(Math.random() * 100)}`);\n    }, 1000);\n  });\n}\n\nasync function loadAllPrices() {\n  for (const id of productIds) {\n    const price = await getPrice(id);\n    console.log(price);\n  }\n  console.log('¡Todos los precios cargados!');\n}\n\nloadAllPrices();
\n\n

Ahora el resultado será:

\n
Precio para 101: $42\n(después de 1 segundo)\nPrecio para 102: $78\n(después de 1 segundo)\nPrecio para 103: $15\n(después de 1 segundo)\n¡Todos los precios cargados!
\n\n

Ventajas: simplicidad, legibilidad, orden predecible.

\n

Desventajas: lento — cada petición espera a que termine la anterior.

Blogs

Book Recommendations