Promesas encadenadas

Métodos then() y catch() encadenados


En ciertas ocasiones es posible que queramos consumir varias promesas encadenadas, es decir, una detrás de otra. Justamente, he escogido el ejemplo del fetch() porque es una de esas situaciones.

Los métodos .then() encadenados

En el caso del fetch(), si pedimos un archivo .json necesitaremos encadenar dos promesas, una para obtener la respuesta del servidor y otra para procesar el contenido JSON (esto depende del método, ocurre así con fetch, pero no con todas las funciones):

fetch("/data.json")
  .then(response => {         // Gestiona la primera promesa
    return response.json();   // Devuelve otra promesa
  })
  .then(data => {             // Gestiona la segunda promesa
    console.log(data);        // Devuelve el JSON como un objeto ya procesado
  })

Es importante que no olvides indicar el return en el primer .then(), ya que de lo contrario no se devuelve la segunda promesa y no se puede encadena con el siguiente .then().

De hecho, usando arrow functions se puede mejorar aún más la legibilidad de este código, recordando que cuando sólo tenemos una sentencia en el cuerpo de la arrow function hay un return implícito:

fetch("/data.json")
  .then(response => response.json())
  .then(data => console.log(data))

Ten en cuenta que este código debería tener otras comprobaciones importantes que estamos omitiendo, ya que nuestra finalidad es entender las promesas, no el método .fetch(), el cuál explicaremos en otro apartado de esta página.

El .catch() en promesas encadenadas

Anteriormente, vimos para que funcionaba .catch() al manejar promesas. Sin embargo, al utilizar varias promesas encadenadas, el método .catch() se puede utilizar de dos formas:

  • 1️⃣ Un .catch() después de cada .then() (para manejar sólo el rechazo del then anterior)
  • 2️⃣ Un sólo .catch() al final (para manejar el rechazo de cualquier then)
// 1) Gestiona los rechazos de cada then()

fetch("/data.json")
  .then(response => {         // Gestiona la primera promesa
    return response.json();   // Devuelve otra promesa
  })
  .catch(error => {           // Gestiona si ocurre un error en el then() anterior
    console.error("Ocurrió un error: ", error);
  })
  .then(data => {             // Gestiona la segunda promesa
    console.log(data);        // Devuelve el JSON como un objeto ya procesado
  })
  .catch(error => {           // Gestiona si ocurre un error en el then() anterior
    console.error("Ocurrió un error: ", error);
  });
// 2) Gestiona los rechazos de todos los then() anteriores

fetch("/data.json")
  .then(response => {         // Gestiona la primera promesa
    return response.json();   // Devuelve otra promesa
  })
  .then(data => {             // Gestiona la segunda promesa
    console.log(data);        // Devuelve el JSON como un objeto ya procesado
  })
  .catch(error => {           // Gestiona si ocurre un error en los then() anteriores
    console.error("Ocurrió un error: ", error);
  });

Puedes utilizar la forma que mejor encaje en tu situación particular.

El método then() y catch() juntos

Existe una tercera forma de hacer las peticiones anteriores. El método then() tiene un segundo parámetro opcional donde se le puede pasar una función callback, que sería la equivalente a la función catch(). Es decir, tendríamos algo similar a lo siguiente:

fetch("/data.json")
  .then(response => {         // Gestiona la primera promesa
    return response.json();   // Devuelve otra promesa
  }, error => {               // Gestiona si ocurre un error en el then() actual
    console.error("Ocurrió un error: ", error);
  })
  .then(data => {             // Gestiona la segunda promesa
    console.log(data);        // Devuelve el JSON como un objeto ya procesado
  }, error => {               // Gestiona si ocurre un error en el then() actual
    console.error("Ocurrió un error: ", error);
  });

Si esta forma te resulta más apropiada para trabajar, puedes utilizarla también. Personalmente me parece un poco menos legible, pero puede que en algunos casos, resulte más apropiada.

El método .finally()

Existe otro método más denominado .finally(), que sirve para pasarle una función callback que se ejecutará cuando la promesa se cumple o se rechace, es decir, se ejecutará cuando llegue a un estado que no sea pendiente. Esto nos ahorrará tener código repetido tanto en el .then() como en el .catch():

fetch("/data.json")
  .then(response => response.json())
  .then(data => console.log(data))
  .finally(() => console.log("Terminado."))
  .catch(error => console.error(data));

Obviamente, se trata de un método opcional, y es conveniente utilizarlo sólo cuando lo necesitemos.

¿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