Async/Await a Promises.all para acelerar llamadas

async

Cambiar Async/Await a Promises.all para acelerar las llamadas API en Node.JS

Async/Await a Promises.all – El Problema

Hay una función de edición masiva en la que el usuario puede seleccionar varios registros y editarlos todos al mismo tiempo.

Sin embargo, al actualizar más de 50 registros a la vez, puede demorar hasta 50 segundos y la página se congela. Los usuarios estaban confundidos durante la espera y, a menudo, abandonaban la página antes de tiempo. Queremos hacerlo más rápido.

async


¿Por qué la lentitud?

El código fue escrito así:

for (r in records) {
await update(r);
}

Actualizar un registro hace una llamada a la API que tarda de 500 ms a 1 s.

Y dado que itera sobre los registros y los actualiza uno por uno, el tiempo aumenta linealmente a medida que hay más registros para actualizar.


Usando Promise para acelerar

Afortunadamente, no tenemos que esperar a que finalice una actualización para procesar la siguiente. Promise.allSettled() nos permite realizar la siguiente solicitud de actualización antes de que finalice la anterior.

Esto es lo que hicimos:

const allPromises = [];for (r in records) {
  const promise = update(r);
  allPromises.push(promise);
};await Promise.allSettled(allPromises);

Visualmente, esto es lo que sucedió al usar async/await vs. usando  promises:

Async

Como podemos ver, dado que no estamos esperando que regrese cada solicitud de actualización antes de realizar la siguiente, ahorra mucho tiempo.


Obtener los resultados de las promesas

Desafortunadamente, update(r) falla ocasionalmente.

Si algunas actualizaciones fallaron, quiero saber cuáles son.

El valor resuelto de Promise.allSettled() puede decirnos eso. Promise.allSettled() me da una serie de objetos, cada uno de los cuales describe el resultado de una promesa.

Si se cumple una promesa, obtengo {status: “fulfilled”, value: xxx }.

Si se rechaza una promesa, obtengo {status: “rejected”, reason: xxx }.

Por ejemplo,

const values = await Promise.allSettled([
  Promise.resolve(33),
  Promise.reject(new Error('an error'))
])
console.log(values)// [
//   {status: "fulfilled", value: 33},
//   {status: "rejected",  reason: Error: an error}
// ]

En mi caso, quiero saber la id de los registros que se actualizaron correctamente y los que no. Así que esto es lo que hicimos:

const allPromises = [];
for (r in records) {
    const promise = new Promise((res, rej) => {
        update(t)
            .then(() => { res(r.id) }); # succeeded
            .catch(() => { rej(r.id) }); # failed
    });
    allPromises.push(promise);
};const outcomes = await Promise.allSettled(allPromises);const succeeded = outcomes.filter(o => o.status === "fulfilled");
const succeededIds = succeeded.map(s => s.value);const failed = outcomes.filter(o => o.status === "rejected");
const failedIds = failed.map(f => f.reason);

Visualmente, esto es lo que sucede cuando fallan algunas promesas:

Async

La matriz devuelta nos dice cuáles tuvieron éxito y cuáles fallaron.

¡Y eso es! Con este cambio, pudimos reducir una operación de edición masiva de más de 50 registros de más de 50 segundos a menos de 5 segundos pasando de Async/Await a Promises.all. 💪


¡Gracias por leer!

Espero que esto sea claro y útil. Si tiene alguna pregunta, no dude en dejar un comentario.

Recent Post