JavaScript async/await

Antes de explicar async/await, necesitamos entender el concepto de programación asíncrona en JavaScript, puedes consultar:Programación asíncrona en JavaScript。

JavaScript es un lenguaje de un solo hilo, lo que significa que solo puede ejecutar una tarea a la vez. Para evitar que las tareas de larga duración bloqueen el hilo principal, JavaScript utiliza un modelo de programación asíncrona.

Asíncrono vs Síncrono

  • Programación síncrona (Synchronous): El código se ejecuta en orden, la siguiente operación solo se ejecuta después de que la anterior se complete
  • Programación asíncrona (Asynchronous): Algunas operaciones se colocan en la "cola de tareas", el hilo principal continúa ejecutando el código posterior, y procesa las tareas de la cola cuando el hilo principal está libre

Ejemplos

// Ejemplo síncrono
console.log('1');
console.log('2');
// Salida: 1, 2

// Ejemplo asíncrono
console.log('1');
setTimeout(() => console.log('2'), 0);
console.log('3');
// Salida: 1, 3, 2

El problema de las funciones de callback

Antes de que apareciera async/await, JavaScript utilizaba principalmente funciones de callback para manejar operaciones asíncronas, pero esto conduce al "Infierno de callbacks" (Callback Hell).

Ejemplos

getData(function(a) {
  getMoreData(a, function(b) {
    getMoreData(b, function(c) {
      getMoreData(c, function(d) {
        console.log(d);
      });
    });
  });
});

Esta estructura anidada hace que el código sea difícil de leer y mantener.


La introducción de Promise

ES6 introdujo el objeto Promise para resolver el problema del infierno de callbacks.

Ejemplos

function getData() {
  return new Promise((resolve, reject) => {
    // Operación asíncrona
    setTimeout(() => resolve('datos'), 1000);
  });
}

getData()
  .then(data => {
    console.log(data);
    return getMoreData(data);
  })
  .then(moreData => {
    console.log(moreData);
  })
  .catch(error => {
    console.error(error);
  });

Aunque Promise mejoró el problema de los callbacks, las llamadas encadenadas con then() todavía no son lo suficientemente intuitivas.


Sintaxis de async/await

ES2017 introdujo async/await, que se basa en Promise, haciendo que el código asíncrono parezca código síncrono.

Función async

Agrega la palabra clave async antes de la declaración de la función, indicando que la función es asíncrona:

async function fetchData() {
  // Cuerpo de la función
}

Una función async siempre devuelve una Promise:

  • Si el valor de retorno no es una Promise, se envolverá automáticamente en una Promise resuelta (resolved)
  • Si se lanza una excepción, devolverá una Promise rechazada (rejected)

Expresión await

await solo se puede usar dentro de funciones async:

async function fetchData() {
  const result = await somePromise;
  console.log(result);
}

await pausará la ejecución de la función async, esperando a que la Promise se complete:

  • Si la Promise se resuelve, devuelve el valor resuelto
  • Si la Promise se rechaza, lanza un error (se puede capturar con try/catch)

Ejemplos de aplicación práctica

Uso básico

Ejemplos

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function showMessage() {
  console.log('Inicio');
  await delay(1000);
  console.log('Después de 1 segundo');
  await delay(1000);
  console.log('Después de otro segundo');
}

showMessage();

Manejo de errores

Ejemplos

async function fetchUserData() {
  try {
    const response = await fetch('https://api.example.com/user');
    if (!response.ok) {
      throw new Error('La respuesta de red no es normal');
    }
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Error al obtener datos:', error);
  }
}

Ejecución en paralelo

Si necesitas ejecutar varias operaciones asíncronas en paralelo, puedes usar Promise.all:

Ejemplos

async function fetchMultipleData() {
  const [userData, productData] = await Promise.all([
    fetch('/api/user'),
    fetch('/api/products')
  ]);
 
  const user = await userData.json();
  const products = await productData.json();
 
  return { user, products };
}

Problemas comunes y mejores prácticas

1. No olvides await

Ejemplos

// Ejemplo incorrecto - olvidar await
async function example() {
  const data = fetch('/api'); // Falta await
  console.log(data); // Salida del objeto Promise
}

// Ejemplo correcto
async function example() {
  const data = await fetch('/api');
  console.log(data); // Salida de datos reales
}

2. Evita async innecesario

Ejemplos

// Innecesario - no hay await dentro de la función
async function unnecessaryAsync() {
  return 42;
}

// Forma más simple de escribir
function simpleFunction() {
  return 42;
}

3. await de nivel superior

En el nivel superior de un módulo, se puede usar await directamente (característica de ES2022):

Ejemplos

// En el módulo
const data = await fetch('/api');
console.log(data);

4. Consideraciones de rendimiento

  • Ejecución secuencial vs ejecución en paralelo: usa Promise.all adecuadamente para mejorar el rendimiento
  • Manejo de errores: asegúrate de que todos los errores posibles sean capturados

Comparación de async/await con Promise tradicional

Característica async/await Promise then/catch
Legibilidad Alta, similar al código síncrono Media, llamadas encadenadas
Manejo de errores Usa try/catch Usa .catch()
Depuración Más fácil, con una pila de llamadas clara Más difícil, la pila de llamadas puede no ser clara
Estructura del código Más plana Anidada o encadenada

Resumen

async/await es una gran mejora en la programación asíncrona de JavaScript, que:

  1. Hace que el código asíncrono sea más legible y mantenible
  2. Se basa en Promise, compatible con el código Promise existente
  3. Proporciona una forma más intuitiva de manejo de errores
  4. Mejora la experiencia de depuración

Aunque async/await no es un reemplazo completo de Promise, en la mayoría de los casos proporciona una solución más elegante. Comprender cómo funciona async/await y las mejores prácticas mejorará enormemente tu capacidad de programación asíncrona en JavaScript.

Otras extensiones