Encadenamiento secuencial de promesas en JavaScript

Técnica para ejecutar varias operaciones asíncronas de forma secuencial asegurando que una tarea comience sólo después de que la anterior haya sido completada con éxito.

Encadenar promesas en Javascript de forma secuencial

¿Qué son las promesas en JavaScript?

Cuando desarrollamos aplicaciones web en Javascript, tarde o temprano necesitaremos ejecutar código asíncrono, y las promesas (Promises en inglés) son la herramienta más poderosa para hacerlo.

Es conveniente usar promesas cuando requerimos un valor que puede no estar disponible inmediatamente, pero que eventualmente se resolverá con un valor o rechazará con un motivo.

Veamos un ejemplo de una promesa:

const miPromesa = new Promise((resolve, reject) => {
  // Ejemplo de operación asíncrona (puede ser una llamada a una API, lectura de archivos, etc.)
  setTimeout(() => {
    const exito = true; // Simulación de éxito o fallo
    if (exito) {
      resolve("¡La operación fue un éxito!");
    } else {
      reject(new Error("¡Algo salió mal!"));
    }
  }, 2000);
});

miPromesa();

Manera común de encadenar promesas

Al ejecutar la promesa anterior, en determinados casos nos podría interesar ejecutar otra operación en caso que miPromesa terminara de forma exitosa. El encadenamiento de promesas implica utilizar los métodos then() y catch() para manejar el resultado o el error de una promesa y devolver otra promesa. Esta técnica permite ejecutar acciones secuenciales de manera clara y legible.

miPromesa
  .then((resultado) => {
    console.log(resultado);
    return otroResultado; // Devuelve otra promesa. otroResultado podría ser una función igual que miPromesa
  })
  .then((otroResultado) => {
    console.log(otroResultado);
    return unTercerResultado; // Devuelve otra promesa
  })
  .then((unTercerResultado) => {
    console.log(unTercerResultado);
  })
  .catch((error) => {
    console.error("¡Hay un error!", error);
  });

En el ejemplo anterior, cada then() maneja el resultado de la promesa anterior y devuelve una nueva promesa, lo que permite encadenar múltiples operaciones asíncronas. Si en algún punto se produce un error en alguna promesa, el flujo de ejecución saltará al primer catch() disponible, lo que hace que sea fácil manejar errores en cualquier etapa del encadenamiento.

Encadenar un número de promesas ilimitado de forma secuencial

El ejemplo anterior requiere escribir manualmente la ejecución secuencial de las promesas, pero, ¿cómo hacerlo si tenemos múltiples promesas que hay que ejecutar de forma dinámica? La clave está en añadir cada tarea en un array [], para luego recorrerla mediante un bucle y ejecutar cada tarea:

// Ejemplo de funciones asíncronas que retornan promesas
function tareaAsincrona1() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log("Tarea asíncrona 1 completada");
      resolve();
    }, 1000);
  });
}

function tareaAsincrona2() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log("Tarea asíncrona 2 completada");
      resolve();
    }, 1500);
  });
}

function tareaAsincrona3() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log("Tarea asíncrona 3 completada");
      resolve();
    }, 800);
  });
}

// Array de funciones asincronas
const tareasAsincronas = [tareaAsincrona1, tareaAsincrona2, tareaAsincrona3];

// Función para ejecutar las promesas de forma secuencial usando async/await
async function ejecutarSecuencial(tareas) {
  for (const tarea of tareas) {
    await tarea();
  }
}

// Llamar las funciones de forma secuencial usando async/await
async function ejecutarTareas() {
  try {
    await ejecutarSecuencial(tareasAsincronas);
    console.log("Se han completado todas las tareas");
  } catch (error) {
    console.error("Ha ocurrido un error:", error);
  }
}

// Invocar la función para ejecutar las tareas
ejecutarTareas();

De este modo encadenamos promesas manera clara, elegante y eficiente. Nos permite controlar el flujo de ejecución y facilitar la composición de operaciones asíncronas.