Artículo original escrito por Dave Gray
Artículo original Async and Await in JavaScript Explained by Making Pizza
Traducido y adaptado por Santiago Yanguas

Async y await pueden parecer complicados... pero son tan fáciles como una tarta de pizza una vez que te sumerges en ellos.

Todos utilizamos async y await en nuestras rutinas diarias.

¿Qué es una tarea asíncrona?

Una tarea asíncrona le permite completar otras tareas mientras la tarea asíncrona sigue trabajando para completarse.

Estos son algunos ejemplos de tareas asíncronas del día a día

Ejemplo 1:

Pides comida en un autoservicio, el cual inicia a procesar su solicitud de comida (una tarea asíncrona).

Avanzas en la fila de conductores (la siguiente tarea), mientras se va preparando su comida.

No había que esperar que la comida estuviera lista para poder avanzar.

Mientras conduces esperas tu comida, la cual se entrega en la ventanilla final.

Ejemplo 2:

Trapeas el suelo de la cocina.

Mientras esperas que se seque el suelo de la cocina, aspiras la alfombra de tu dormitorio.

La tarea original era limpiar el suelo de la cocina y la tarea está completa cuando el suelo está seco.

Estar de pie esperando a que se seque el suelo no es demasiado productivo, así que aspiras el suelo del dormitorio mientras se seca el de la cocina.

Así es como JavaScript maneja también las funciones asíncronas.

Ejemplo de Async/Await - Hornear una pizza congelada

Decides hornear una pizza en tu horno y tu primer paso es precalentar el horno.

Así que sé fijas la temperatura deseada y empiezas a precalentar el horno.

Mientras se precalienta el horno, sacas la pizza del congelador, abres la caja y la pones en una sartén para pizza.

¡Te queda tiempo!

Tal vez, puedes tomar una bebida y ver algo de televisión mientras esperas que el horno emita un ¡beep!, cuando esté listo.

A continuación se muestra un código para simular este ejemplo:

// Esta función asíncrona simula la respuesta del horno
const hornoListo = async () => {
  return new Promise(resolve => setTimeout(() => {
    resolve('Beep! Horno Calentado!')
  }, 3000));
}

// Definir la función asíncrona preCalentarHorno
const preCalentarHorno = async () => {
  console.log('Pre calentar horno.');
  const respuesta = await hornoListo();
  console.log(respuesta);
}

// Definir las otras funciones
const obtenerPizzaFria = () => console.log('Obtener pizza.');
const abrirPizzaFria = () => console.log('Abrir pizza.');
const obtenerSartenPizza = () => console.log('Obtener sarten.');
const ponerPizzaSarten = () => console.log('Poner pizza en el sarten.');
const tomarRefresco = () => console.log('Tomar un refresco.');
const mirarTV = () => console.log('Ver la televisión.');

// Ahora llama a las funciones
preCalentarHorno();
obtenerPizzaFria();
abrirPizzaFria();
obtenerSartenPizza();
ponerPizzaSarten();
tomarRefresco();
mirarTV();

// Secuencia de salida en la consola:
Pre calentar horno.
Obtener pizza.
Abrir pizza.
Obtener sarten.
Poner pizza en el sarten.
Tomar un refresco.
Ver la televisión.
Beep! Horno Calentado!
Calentar pizza, mientras tomo un refresco y ver televisión

El proceso anterior es exactamente lo que significa async y await.

Mientras await(esperamos) a que se complete la función asíncrona preCalentarHorno, podemos realizar tareas síncronas como obtenerPizzaFria, abrirPizzaFria, obtenerSartenPizza, ponerPizzaSarten, tomarRefresco e incluso mirarTV.

Realizamos tareas asíncronas casi todo el tiempo

Así es como funciona también el JavaScript asíncrono.

Fíjate que cuando await(esperamos) la respuesta de una función async(asíncrona) esta debe ser llamada dentro de otra función async(asíncrona). Eso es lo que vemos arriba cuando hornoListo es llamado dentro de preCalentarHorno.

Hay que recordar dos puntos clave:

  1. JavaScript NO esperará a que una función async(asíncrona) como preCalentarHorno se complete antes de pasar a las tareas que le siguen como obtenerPizzaFria y abrirPizzaFria.
  2. JavaScript await(esperará) a que una función async(asíncrona) como hornoListo se complete, devuelva datos antes de pasar a la siguiente tarea dentro de una función asíncrona padre. Vemos esto cuando la sentencia console.log(respuesta) no se ejecuta hasta que hornoListo haya devuelto una respuesta.

Si el ejemplo de la pizza no es suficiente...

Sé que los ejemplos cotidianos nos ayudan a algunos, aunque otros pueden preferir el código real.

Por lo tanto, voy a proporcionar un ejemplo menos abstracto de JavaScript async y await que solicita datos con la API Fetch:

Ejemplo de código Async/Await en JavaScript

const obtenerDatos = async () => {
    try {
    const respuesta = await fetch('https://jsonplaceholder.typicode.com/users');
    if (!response.ok) throw Error();
    const datos = await response.json();
    // Hacer algo con estos datos... guardar en la db, actualizar el DOM, etc.
    console.log(datos);
    console.log('Verás esto último.')
    } catch (err) {
        console.error(err);
    }
} 

obtenerDatos();
console.log('Esto es lo primero que verás.');
Obtener en JavaScript

Conclusión

Espero haberte ayudado a entender async y await en JavaScript.

Sé que puede llevar un tiempo comprenderlo completamente.

Empieza a precalentar la pizza que se te antoja en tu horno y mira algunos ejemplos más de async y await mientras esperas él ¡beep!

Dejo un tutorial de mi canal de YouTube. El video da una explicación más profunda, con más ejemplos de código, incluyendo una discusión de callbacks, promesas, resoluciones y la API Fetch junto con async & await: