// js · Web Platform Advent #3
async/await en JavaScript: guía práctica
Cómo funcionan async y await, su relación con las promesas, el manejo de errores con try/catch, await en bucles y el error de serie-vs-paralelo — con ejemplos ejecutables.
Las palabras clave async y await son sintaxis sobre las promesas. Te permiten escribir código asíncrono que se lee de arriba abajo como código síncrono, sin dejar de ser no bloqueante por debajo. Si conoces las promesas, ya conoces async/await — solo elimina la ceremonia de los .then().
Lo básico
Marca una función como async y podrás usar await dentro de ella. await pausa esa función hasta que la promesa se resuelve y te da el valor resuelto:
async function getUser(id) {
const res = await fetch('/api/user/' + id);
const user = await res.json();
return user.name;
}
getUser(42).then((name) => console.log(name)); Dos cosas que recordar: una función async siempre devuelve una promesa (lo que devuelvas se envuelve en una), y await solo funciona dentro de una función async — salvo en el nivel superior de un módulo ES, donde se permite el top-level await.
async/await vs promesas
Es la misma maquinaria. Esta cadena de promesas…
fetch('/api/user')
.then((res) => res.json())
.then((user) => console.log(user.name)); …es exactamente equivalente a esta versión async, que a la mayoría le resulta más fácil de leer y depurar:
const res = await fetch('/api/user');
const user = await res.json();
console.log(user.name); Puedes mezclarlas libremente — await cualquier promesa, incluido el resultado de Promise.all().
Manejo de errores con try/catch
Con async/await capturas las promesas rechazadas con un try/catch normal, la misma construcción que usas para los errores síncronos:
async function loadUser(id) {
try {
const res = await fetch('/api/user/' + id);
if (!res.ok) throw new Error('HTTP ' + res.status);
return await res.json();
} catch (err) {
console.error('No se pudo cargar el usuario:', err.message);
return null;
} finally {
console.log('loadUser terminado');
}
} Un await rechazado lanza una excepción en esa línea, así que se ejecuta el bloque catch. El bloque finally se ejecuta haya o no habido un error.
El gran error: await en serie vs en paralelo
El fallo más común con async/await es esperar tareas independientes una tras otra. Esto se ejecuta en serie — la segunda petición no empieza hasta que termina la primera:
// Lento: tiempo total = a + b + c
const a = await fetchA();
const b = await fetchB();
const c = await fetchC(); Si las tareas no dependen entre sí, lánzalas todas primero y luego espera juntas con Promise.all. Ahora se ejecutan en paralelo:
// Rápido: tiempo total ≈ la más lenta
const [a, b, c] = await Promise.all([fetchA(), fetchB(), fetchC()]); await en bucles
Un bucle for normal con await procesa los elementos de uno en uno, lo cual es correcto cuando cada paso depende del anterior o debes evitar saturar un servidor:
for (const id of ids) {
const item = await fetchItem(id); // secuencial — uno tras otro
console.log(item);
} Para procesarlos en paralelo, transforma en un array de promesas y await Promise.all. Ojo: forEach no espera a los callbacks async — usa map + Promise.all en su lugar:
const items = await Promise.all(ids.map((id) => fetchItem(id))); Referencia rápida
| Objetivo | Patrón |
|---|---|
| Esperar una promesa | const x = await p; |
| Manejar errores | try { await ... } catch (e) { ... } |
| Tareas independientes en paralelo | await Promise.all([...]) |
| Bucle secuencial | for...of con await |
| Bucle concurrente | map → Promise.all |
async/await no reemplaza a las promesas — las hace legibles. Mantén Promise.all a mano para el paralelismo, recurre a try/catch para los errores y vigila el await en serie accidental, y tendrás el JavaScript asíncrono bajo control.