Manejar promesas en grupo

Controlar y esperar a grupos de promesas


Si nos encontramos en una situación donde queremos controlar un grupo de promesas o necesitamos un alto nivel de control al manejar promesas, podemos utilizar el objeto Promise de Javascript, que incorpora varios métodos estáticos interesantes.

Observa la siguiente tabla:

Métodos Descripción
Promise.all(list) Acepta sólo si todas las promesas del se cumplen.
Promise.allSettled(list) Acepta sólo si todas las promesas del se cumplen o rechazan.
Promise.any(list) Acepta con el valor de la primera promesa del que se cumpla.
Promise.race(list) Acepta o rechaza según la primera promesa del que se procese.
Métodos estáticos directos
Promise.resolve(value) Devuelve una promesa cumplida directamente con el dado.
Promise.reject(value) Devuelve una promesa rechazada directamente con el dado.

Vamos a ver un ejemplo con cada uno de estos métodos, realizando varias peticiones y descargando varios archivos diferentes que necesitaremos para nuestras tareas.

El método Promise.all()

El método Promise.all() funciona como un «todo o nada»: le pasas un grupo de varias promesas y el Promise.all() te devolverá una promesa que se cumplirá cuando todas las promesas del grupo se hayan cumplido. Si alguna de ellas se rechaza, la promesa de Promise.all() también lo hará.

Observa este ejemplo:

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

const tasks = [task1, task2, task3];

// Utilizando async/await
const responses = await Promise.all(tasks);

const codes = responses.map(response => response.status); // [200, 200, 200]
const task1 = fetch("/robots.txt");
const task2 = fetch("/index.css");
const task3 = fetch("/index.js");

const tasks = [task1, task2, task3];

// Utilizando .then()
Promise.all(tasks)
  .then(responses => {
    const codes = responses.map(response => response.status);
    console.log(codes); // [200, 200, 200]
  });
  • 1️⃣ Realizamos 3 fetch(), donde cada uno devuelve una promesa.
  • 2️⃣ Almacenamos esas 3 promesas en un tasks.
  • 3️⃣ Al hacer un Promise.all(tasks) devolvemos una nueva promesa.
  • 4️⃣ Dicha promesa se cumplirá, si todas las que pasamos en el array se cumplen invidiualmente.
  • 5️⃣ En el caso de que alguna de las 3 se rechace, la promesa del Promise.all() también se rechaza.

Observa en las pestañas, que también se podría realizar utilizando .then() en lugar de async/await.

El método Promise.allSettled()

El método Promise.allSettled() funciona como un «todas procesadas»: devuelve una promesa que se cumple cuando todas las promesas del se hayan procesado, independientemente de que se hayan cumplido o rechazado.

const task1 = fetch("/robots.txt");
const task2 = fetch("https://google.com/index.css");
const task3 = fetch("/index.js");

const tasks = [task1, task2, task3];

const results = await Promise.allSettled(tasks);  // [{}, {}, {}]

Esta operación nos devuelve un de objetos (uno por cada promesa) donde cada objeto tiene dos propiedades:

  • La propiedad status, donde nos indica si cada promesa individual ha sido cumplida o rechazada
  • La propiedad value, con los valores devueltos por la promesa si se cumple.
  • La propiedad reason, con la razón del rechazo de la promesa si no se cumple.

En este caso, obtendremos que la primera y última promesa se resuelven (fulfilled), mientras que la segunda nos da un error de CORS y se rechaza (rejected).

El método Promise.any()

El método Promise.any() funciona como «la primera que se cumpla»: Devuelve una promesa con el valor de la primera promesa individual del que se cumpla. Si todas las promesas se rechazan, entonces devuelve una promesa rechazada.

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

const tasks = [task1, task2, task3];

const response = await Promise.any(tasks);  // Response

Como vemos, en este caso Promise.any() no devolverá un , sino que devolverá el dato de la primera promesa que se ha cumplido.

El método Promise.race()

El método Promise.race() funciona como una «la primera que se procese»: la primera promesa del que sea procesada, independientemente de que se haya cumplido o rechazado, determinará la devolución de la promesa del Promise.race(). Si se cumple, devuelve una promesa cumplida, en caso negativo, devuelve una rechazada.

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

const tasks = [task1, task2, task3];

const response = await Promise.race([task1, task2, task3]);

De forma muy similar a la anterior, Promise.race() devolverá la promesa que se resuelva primero, ya sea cumpliéndose o rechazándose.

Promesas estáticas

Mediante los métodos estáticos Promise.resolve() y Promise.reject() podemos devolver una promesa cumplida o rechazada respectivamente sin necesidad de crear una promesa con new Promise(), algo que podría ser interesante o cómodo en algunos casos.

Observa que la siguiente función doTask() no es asíncrona:

const doTask = () => {
  const number = 1 + Math.floor(Math.random() * 6);
  const isEven = number % 2 === 0;

  return isEven ? Promise.resolve(number) : Promise.reject(number);
}

En este caso, generamos un número aleatorio y se devuelve una promesa. Cuando el número generado es par se cumple la promesa, cuando es impar, se rechaza. Sin embargo, ten en cuenta que el problema en este caso es que la promesa no «envuelve» toda la función, por lo que si la tarea tardase algún tiempo en generar el número, no podríamos utilizar el .then() para consumir la promesa.

Estas funciones estáticas se suelen utilizar en muy pocos casos, para mantener cierta compatibilidad en funciones que se espera que devuelvan una promesa.

¿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