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 dePromise.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 constatus
yvalue/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 dePromise.any
.Promise.resolve(value)
: crea una promesa resuelta con elvalue
dado.Promise.reject(error)
: crea una promesa rechazada con elerror
dado.
Promise.all
es probablemente el más común en la práctica.