Aunque JavaScript utiliza un modelo de ejecución de un solo hilo (single-threaded), es capaz de gestionar miles de operaciones simultáneas como peticiones a APIs, lectura de archivos o eventos del usuario. Esto es posible gracias a la programación asíncrona. Entender el funcionamiento de las Promesas y la sintaxis async/await es un requisito indispensable para cualquier desarrollador web moderno.

La evolución del flujo asíncrono: De Callbacks a Async/Await

La era de los Callbacks

El infierno de los Callbacks (Callback Hell)
// La anidación profunda vuelve el código ilegible
obtenerUsuario(userId, (err, user) => {
  if (err) return gestionarError(err);
  obtenerPedidos(user.id, (err, orders) => {
    if (err) return gestionarError(err);
    obtenerDetallesPedido(orders[0].id, (err, details) => {
      if (err) return gestionarError(err);
      console.log(details); // ¡Por fin!
    });
  });
});

Promises: Una forma más elegante

Encadenamiento de Promesas
obtenerUsuario(userId)
  .then(user => obtenerPedidos(user.id))
  .then(orders => obtenerDetallesPedido(orders[0].id))
  .then(details => console.log(details))
  .catch(err => gestionarError(err))
  .finally(() => ocultarCargador());

Async/Await: Legibilidad superior

Sintaxis moderna Async/Await
async function obtenerPedidoCompleto(userId) {
  try {
    const user = await obtenerUsuario(userId);
    const orders = await obtenerPedidos(user.id);
    const details = await obtenerDetallesPedido(orders[0].id);
    return details;
  } catch (err) {
    gestionarError(err);
  } finally {
    ocultarCargador();
  }
}

Creación de Promesas desde cero

Uso del constructor Promise
function esperar(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

function peticionConTiempoLimite(url, ms) {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => reject(new Error("Tiempo excedido")), ms);

    fetch(url)
      .then(response => {
        clearTimeout(timer);
        resolve(response);
      })
      .catch(err => {
        clearTimeout(timer);
        reject(err);
      });
  });
}

Gestión de operaciones concurrentes

Promise.all — Ejecución en paralelo
// Todas deben resolverse con éxito (falla ante el primer error)
const [usuarios, productos, pedidos] = await Promise.all([
  fetch("/api/usuarios").then(r => r.json()),
  fetch("/api/productos").then(r => r.json()),
  fetch("/api/pedidos").then(r => r.json()),
]);

// allSettled — Retorna el estado de cada promesa, aunque algunas fallen
const resultados = await Promise.allSettled([
  fetch("/api/usuarios"),
  fetch("/api/productos"), 
  fetch("/api/pedidos"),   
]);

resultados.forEach(res => {
  if (res.status === "fulfilled") {
    console.log("Éxito:", res.value);
  } else {
    console.log("Error:", res.reason);
  }
});
Promise.race — La primera en terminar gana
// Muy útil para timeouts o carreras de red
const resultado = await Promise.race([
  fetch("/api/datos"),
  esperar(5000).then(() => { throw new Error("Tiempo agotado!"); })
]);

Patrones efectivos para manejo de errores

Wrapper estilo Go (sin bloques try/catch masivos)
// Retorna una tupla [error, datos]
async function to(promesa) {
  try {
    const data = await promesa;
    return [null, data];
  } catch (err) {
    return [err, null];
  }
}

// Uso práctico
const [err, user] = await to(obtenerUsuario(123));
if (err) {
  console.error("Error al obtener el usuario:", err.message);
  return;
}
console.log(user);

Errores frecuentes que debes evitar

  • Olvidar el await: Sin la palabra clave, obtendrás el objeto Promise en lugar de su resultado final.
  • Secuencialidad innecesaria: No ejecutes promesas de forma lineal si son independientes; usa Promise.all() para mejorar el rendimiento.
  • Ignorar rechazos: Todo proceso asíncrono debe tener un bloque catch o una gestión centralizada de errores.
  • Async en bucles forEach: Este método no espera a las promesas. Utiliza un bucle for...of o Promise.all(arr.map(...)).
  • Promesas redundantes: Si una librería ya retorna una Promise, no necesitas envolverla de nuevo en un new Promise().

Prueba nuestras herramientas gratuitas para desarrolladores

Optimiza y embellece tu código JavaScript al instante.