EcmaScript 6

Durante la escritura de este artículo ningún callback ha sufrido daño alguno.

Introducción

Para terminar de hablar de promesas en JavaScript (al menos por una temporada), veremos la forma de olvidarnos de los callbacks y comenzar a programar usando async y await.

Seguramente, habrás oído hablar del infierno de los callbacks. No, ahí no es donde van los callbacks que se portan mal, sino que con esa expresión hacemos referencia a las ejecuciones de código asíncronas en JavaScript, que se van enlazando mediante callbacks.

Por desgracia es común encontrar aún código con este aspecto:

function requestCompleteUserInformation(userId) {
  const userInformation = {
    userId
  };

  this.userService.requestBasicUser(userId).then((userBasicInformation) => {
    userInformation.basicInformation = userBasicInformation;

    this.userService.requestuserAddress(userId).then((userAddress) => {
      userInformation.address = userAddress;

      this.userService.requestUserPhones(userId).then((userPhones) => {
        userInformation.phones = userPhones;

        // Etcétera, etcétera
      }).catch(() => {})
    }).catch(() => {})
  }).catch(() => {})
}

En el fragmento anterior no he añadido mucho código; pero imaginad lo que se complicaría, si además de todos esos callbacks tuviéramos más código de por medio. Ese es uno de los motivos por los que podemos usar async...await.

Otro motivo, por ejemplo, puede ser por claridad. En el caso de que no nos guste tener callbacks, ya sea definidos inline o como llamadas a funciones. Pero realmente, async...await es una herramienta que viene a ayudarnos en ciertos casos, y puede depender mucho de las preferencias de quién esté programando, el usarlos o no.

Primer ejemplo

Para comenzar veamos el código anterior traducido a async...await:

async function requestCompleteUserInformation(userId) {
  const userInformation = {
    userId
  };

  try {
    userInformation.basicInformation = await this.userService.requestBasicUser(userId);
    userInformation.address = await this.userService.requestuserAddress(userId);
    userInformation.phones = await this.userService.requestUserPhones(userId);
  } catch (error) {
    // Código para la gestión de errores
  }
}

Vale, "nos has puesto dos ejemplos de código, y el segundo parece que queda más bonito, pero ¿qué hace?". Tranquilos y tranquilas que ya voy a la explicación 😀.

¿Qué hacen async y await?

Cuando tenemos una promesa, en lugar de que tener que usar callbacks como hasta ahora, podemos utilizar la palabra clave await delante de la promesa. Esto hará que el código quede esperando a que termine la promesa, siempre se suele decir que permite tener código asíncrono, definido como código síncrono.

Si la promesa se resuelve y además, lo hace con un valor, podremos asignarlo (como en las líneas 7, 8 y 9 del código anterior).

Si la promesa es rechazada, se lanzará como excepción. De forma que tendremos que envolver la o las sentencias que usen await, con un bloque try...catch si queremos controlar las excepciones rechazadas. Puede que nos interese que estas excepciones se controlen en un bloque superior, por lo que no es obligatorio poner el try...catch junto al await. Pero eso sí, recuerda que si la promesa puede ser rechazada, en alguna parte del código deberías capturarla.

Recapitulando, tenemos await que se espera a que termine una promesa, y los bloques try...catch para capturar las promesas rechazadas. Entonces, ¿para que sirve async? Pues bien, await sólo se puede utilizar en funciones que haya declarado como asíncronas (obsérverse la primera línea del código anterior). Es decir, no se puede utilizar await fuera de una función que no sea asíncrona.

Con todo lo anterior, ahora puedes volver a subir al código que usa async...await y revisarlo. Si me he explicado bien en los párrafos anteriores, ahora entenderás un poquito más el funcionamiento de éstas palabras.

Detalle extra sobre las funciones asíncronas

Así que prácticamente, eso sería todo, o al menos, eso es lo que suelen explicarse casi siempre. Pero aparte las funciones asíncronas tienen una peculiaridad más que mucha gente omite, y es que devuelven automáticamente una promesa.

async function voidPromise() {
  await anotherAsyncFunction();
  // Automáticamente se transforma en return Promise.resolve();
  // Lo mismo aplicaría si no hacemos ningún return.
  return;
}

async function promiseWithData() {
  await anotherAsyncFunction();
  // Automáticamente se transforma en return Promise.resolve(value);
  return value;
}

Con esto en mente, podemos esperar a que termine una función que ya es asíncrona sin tener que hacer código extra:

// Se podría haber hecho return del await, pero he preferido definir constantes
// intermedias para que podáis ver de forma más sencilla que estamos devolviendo
// un objeto y no una promesa.

async function loadUser(userId) {
  const user = await this.userService.getById(userId);
  return user;
}

async function loadUserConfig(userId) {
  const userConfig = await this.userService.getConfigByUser(userId);
  return userConfig;
}

// Obsérvese como podemos usar await con las funciones sin ningún problema.
async function getUserAndConfig(userId) {
  const user = await loadUser(userId);
  const userConfig = await loadUserConfig(userId);
}

Ahora sí, con esto terminamos nuestro repaso a las promesas en JavaScript. Como siempre, espero que os haya podido servir de ayuda, y cualquier pregunta que tengáis podéis dejarla en comentarios.


Para ampliar algo de información sobre las promesas podéis recurrir a los siguientes libros:

Comparte este artículo con quien quieras
Repromise: Nueva librería para promesas JavaScript
Convertir una imagen en caracteres con JavaScript

Leave a Comment

Your email address will not be published. Required fields are marked *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.