Skip to content

L53 - Async / Await

async y await son sugar syntax sobre Promesas que permite escribir código asíncrono con una apariencia síncrona. Fue introducido en ES2017 (ES8) y hoy es el estándar de facto para manejar asincronía en JavaScript.

Declarar una función como async hace que siempre devuelva una Promise:

async function saludar() {
return 'Hola'; // automáticamente: Promise.resolve('Hola')
}
saludar().then(console.log); // 'Hola'

Si la función lanza una excepción, devuelve una Promise rechazada:

async function fallar() {
throw new Error('Oops');
}
fallar().catch(console.error); // Error: Oops

await pausa la ejecución de la función async hasta que la Promise se resuelva o rechace:

async function obtenerUsuario(id) {
const respuesta = await fetch(`/api/usuarios/${id}`);
const usuario = await respuesta.json();
return usuario;
}

⚠️ Solo se puede usar await dentro de una función async (salvo top-level await en módulos).

Para manejar errores se usa try/catch tradicional:

async function cargarDatos() {
try {
const res = await fetch('/api/datos');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const datos = await res.json();
return datos;
} catch (error) {
console.error('Error al cargar datos:', error);
return null;
}
}
const datos = await cargarDatos().catch(err => {
console.error(err);
return null;
});
async function secuencial() {
const a = await fetch('/api/a'); // espera
const b = await fetch('/api/b'); // espera de nuevo
// Tiempo total: suma de ambos tiempos
}
async function paralelo() {
const [a, b] = await Promise.all([
fetch('/api/a'),
fetch('/api/b'),
]);
// Tiempo total: el mayor de los dos tiempos
}

🎯 Regla de oro: si las operaciones no dependen entre sí, ejecútalas en paralelo con Promise.all().

async function obtenerDashboard(usuarioId) {
try {
// Paralelo: datos independientes
const [usuario, notificaciones] = await Promise.all([
fetch(`/api/usuarios/${usuarioId}`).then(r => r.json()),
fetch(`/api/notificaciones/${usuarioId}`).then(r => r.json()),
]);
// Secuencial: depende del usuario
const pedidos = await fetch(`/api/pedidos/${usuario.id}`).then(r => r.json());
return { usuario, notificaciones, pedidos };
} catch (error) {
console.error('Dashboard falló:', error);
throw error;
}
}
Async/await.then()
Más legible, parece síncronoMás verboso con cadenas largas
try/catch familiar.catch() anidado
Depuración más fácil (breakpoints)Menos intuitivo en depuración
Ideal para flujos complejosÚtil para transformaciones rápidas

Ambos son igual de válidos. async/await es la opción recomendada en la mayoría de los casos por su claridad.

En módulos se puede usar await sin estar dentro de una función async:

// config.js (módulo)
const response = await fetch('/config.json');
export const config = await response.json();

El módulo espera a que la Promise se resuelva antes de que otros módulos puedan importarlo. Muy útil para inicialización asíncrona.

⚠️ El top-level await bloquea la carga del módulo. Úsalo con cuidado para no retrasar la aplicación innecesariamente.

  1. Siempre usa try/catch o proporciona un .catch().
  2. Paraleliza operaciones independientes con Promise.all().
  3. No abuses de await secuencial si no es necesario.
  4. Los bucles for...of con await se ejecutan secuencialmente; para paralelo usa Promise.all() con un .map().
// Secuencial (uno tras otro)
for (const url of urls) {
const res = await fetch(url);
}
// Paralelo (todos a la vez)
const resultados = await Promise.all(urls.map(url => fetch(url)));