El map() y async/await

No utilices async/await dentro de map()


En el artículo anterior, vimos los problemas que podemos experimentar cuando trabajamos con múltiples promesas y necesitamos esperar a tener los resultados.

El problema de map() con async/await

Una de las opciones que podemos escoger sería utilizar async/await para resolver el grupo de promesas que tenemos almacenado en un array y que recorremos con un .map() para generar un nuevo array de resultados:

const task1 = fetch("/robots.txt");   // fetch devuelve una promise
const task2 = fetch("/theme.css");    // fetch devuelve una promise
const task3 = fetch("/index.js");     // fetch devuelve una promise

const tasks = [task1, task2, task3];  // Array de promises

// Recorrer el array de promesas para resolverlas con await
// y obtener un array derivado (map) con los datos resueltos
const responses = tasks.map(async (promise) => {
  return await promise
});

responses     // [Promise, Promise, Promise]

Aunque el planteamiento es correcto y no nos dará error, este código no funciona porque await sólo se puede utilizar dentro de una función async y los .map() son síncronos y no esperan a que se resuelvan las promesas.

Alternativa 1: Usar un bucle for

Una alternativa posible es usar un bucle for para iterar sobre el array de promesas, resolverlas con await, y generar un nuevo array con los resultados resueltos:

const task1 = fetch("/robots.txt");
const task2 = fetch("/theme.css");
const task3 = fetch("/index.js");

const tasks = [task1, task2, task3];

const responses = [];
for (let i = 0; i < tasks.length; i++) {
  const promise = tasks[i];
  responses[i] = await promise;
}

responses     // [Response, Response, Response]

Como puedes ver, en este caso no tendríamos problema. SIn embargo, el código es difícil de leer.

Alternativa 2: Usar un bucle for...of

De forma muy similar a la anterior, podemos utilizar un bucle for...of:

const task1 = fetch("/robots.txt");
const task2 = fetch("/theme.css");
const task3 = fetch("/index.js");

const tasks = [task1, task2, task3];

const responses = [];
for (const promise of tasks) {
  responses.push(await promise);
}

responses     // [Response, Response, Response]

En este caso, la sintaxis es un poco más elegante y legible.

Alternative 3: Usar Promise.all()

Javascript tiene varios métodos estáticos en Promise para manejar promesas en grupo. Promise.all() es uno de los más comunes:

const task1 = fetch("/robots.txt");
const task2 = fetch("/theme.css");
const task3 = fetch("/index.js");

const tasks = [task1, task2, task3];

const responses = await Promise.all(tasks);

responses     // [Response, Response, Response]

Como puedes ver, la sintaxis es muy sencilla y nos permite esperar a que se resuelvan todas las promesas del array tasks. Una vez resueltas, las respuestas se almacenan en un nuevo array, que hemos llamado responses.

En el siguiente artículo aprenderemos a utilizar tanto Promise.all() en profundidad, como otros métodos para manejar promesas en grupo.

¿Quién soy yo?

Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.

Puedes encontrar más sobre mi en Manz.dev