preloader

Promise API en JavaScript

Hay 6 métodos estáticos en la clase Promise. A continuación, exploraremos sus casos de uso.

Promise.all

Supongamos que queremos ejecutar varias promesas en paralelo y esperar hasta que todas ellas se completen. Por ejemplo, descargar múltiples URLs simultáneamente y procesar su contenido una vez que todas hayan terminado.

Para esto, usamos Promise.all.

La sintaxis es:

				
					let promise = Promise.all(iterable);

				
			

Promise.all toma un iterable (usualmente un array de promesas) y devuelve una nueva promesa.

Esta nueva promesa se resuelve cuando todas las promesas listadas se resuelven, y el array de esos resultados se convierte en su resultado.

Por ejemplo, el Promise.all siguiente se resuelve después de 3 segundos, y su resultado es un array [1, 2, 3]:

				
					Promise.all([
  new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1
  new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2
  new Promise(resolve => setTimeout(() => resolve(3), 1000))  // 3
]).then(alert); // 1,2,3 cuando las promesas están listas: cada promesa constituye un miembro del array

				
			

Es importante notar que el orden de los elementos en el array es el mismo que el de las promesas originales, independientemente de cuándo se resuelvan.

Un truco común es mapear un array de datos de trabajo dentro de un array de promesas, y luego envolverlos dentro de un Promise.all.

Por ejemplo, si tenemos un array de URLs, podemos usar fetch en todas ellas así:

				
					let urls = [
  'https://api.github.com/users/iliakan',
  'https://api.github.com/users/remy',
  'https://api.github.com/users/jeresig'
];

// "mapear" cada url a la promesa de su fetch
let requests = urls.map(url => fetch(url));

// Promise.all espera hasta que todas las tareas estén resueltas
Promise.all(requests)
  .then(responses => responses.forEach(
    response => alert(`${response.url}: ${response.status}`)
  ));

				
			

Otro ejemplo con fetch: buscar información de usuario para un array de usuarios de GitHub por sus nombres (o podríamos buscar un array de bienes por sus “id”, la lógica es la misma):

				
					let names = ['iliakan', 'remy', 'jeresig'];

let requests = names.map(name => fetch(`https://api.github.com/users/${name}`));

Promise.all(requests)
  .then(responses => {
    // todas las respuestas son resueltas satisfactoriamente
    for(let response of responses) {
      alert(`${response.url}: ${response.status}`); // muestra 200 por cada url
    }

    return responses;
  })
  // mapear el array de resultados dentro de un array de response.json() para leer sus contenidos
  .then(responses => Promise.all(responses.map(r => r.json())))
  // todas las respuestas JSON son analizadas: "users" es el array de ellas
  .then(users => users.forEach(user => alert(user.name)));

				
			

Si cualquiera de las promesas es rechazada, la promesa devuelta por Promise.all se rechaza inmediatamente: “reject” con ese error.

Por ejemplo:

				
					Promise.all([
  new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
  new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).catch(alert); // Error: Whoops!

				
			

Aquí, la segunda promesa se rechaza en dos segundos. Esto lleva a un rechazo inmediato de Promise.all, entonces .catch se ejecuta: el error del rechazo se vuelve la salida de todo Promise.all.

Promise.allSettled

Una adición reciente al lenguaje que permite esperar a que todas las promesas se resuelvan sin importar sus resultados. El array resultante tiene:

  • {status:"fulfilled", value:result} para respuestas exitosas,
  • {status:"rejected", reason:error} para errores.

Por ejemplo, queremos hacer “fetch” de la información de múltiples usuarios. Incluso si uno falla, aún estamos interesados en los otros.

Usemos Promise.allSettled:

				
					let urls = [
  'https://api.github.com/users/iliakan',
  'https://api.github.com/users/remy',
  'https://no-such-url'
];

Promise.allSettled(urls.map(url => fetch(url)))
  .then(results => {
    results.forEach((result, num) => {
      if (result.status == "fulfilled") {
        alert(`${urls[num]}: ${result.value.status}`);
      }
      if (result.status == "rejected") {
        alert(`${urls[num]}: ${result.reason}`);
      }
    });
  });

				
			

El results será:

				
					[
  {status: 'fulfilled', value: ...response...},
  {status: 'fulfilled', value: ...response...},
  {status: 'rejected', reason: ...error object...}
]

				
			

Promise.race

Similar a Promise.all, pero espera solo por la primera respuesta y obtiene su resultado (o error).

La sintaxis es:

				
					let promise = Promise.race(iterable);

				
			

Por ejemplo, aquí el resultado será 1:

				
					Promise.race([
  new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
  new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).then(alert); // 1

				
			

Promise.any

Es similar a Promise.race, pero espera solo por la primera promesa cumplida y obtiene su resultado. Si todas las promesas fueron rechazadas, entonces la promesa devuelta es rechazada con AggregateError, un error especial que almacena los errores de todas las promesas en su propiedad errors.

La sintaxis es:

				
					let promise = Promise.any(iterable);

				
			

Por ejemplo, aquí el resultado será 1:

				
					Promise.any([
  new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 1000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(1), 2000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).then(alert); // 1

				
			

Promise.resolve/reject

Estos métodos son raramente necesitados en código moderno debido a la sintaxis async/await.

Promise.resolve

Promise.resolve(value) crea una promesa resuelta con el resultado value.

Por ejemplo:

				
					let promise = new Promise(resolve => resolve(value));

				
			

Promise.reject

Promise.reject(error) crea una promesa rechazada con error.

Por ejemplo:

				
					let promise = new Promise((resolve, reject) => reject(error));

				
			

Resumen

Existen 6 métodos estáticos en la clase Promise:

  • Promise.all(promises): espera que todas las promesas se resuelvan y devuelve un array de sus resultados. Si alguna es rechazada, se vuelve el error de Promise.all y los demás resultados son ignorados.
  • Promise.allSettled(promises): espera que todas las promesas respondan y devuelve sus resultados como un array de objetos con status y value/reason.
  • Promise.race(promises): espera a la primera promesa que responda y ese resultado o error se vuelve su resultado o error.
  • Promise.any(promises): espera por la primera promesa que se cumpla y devuelve su resultado. Si todas las promesas son rechazadas, AggregateError se vuelve el error de Promise.any.
  • Promise.resolve(value): crea una promesa resuelta con el value dado.
  • Promise.reject(error): crea una promesa rechazada con el error dado.

Promise.all es probablemente el más común en la práctica.

Related Post

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *