Async/Await
Async/Await es azúcar sintáctica sobre Promises que hace que el código asíncrono se vea y se comporte más como código síncrono. Es la forma más moderna y legible de manejar asincronismo en JavaScript.
¿Qué es Async/Await?
Async/await son dos palabras clave que trabajan juntas:
async : Declara que una función es asíncrona (retorna una Promise)
await : Pausa la ejecución hasta que una Promise se resuelva
async function obtenerDatos () {
const respuesta = await fetch ( '/api/data' );
const datos = await respuesta . json ();
return datos ;
}
await solo puede usarse dentro de funciones async. Intentar usarlo fuera generará un error de sintaxis.
Funciones Async
Una función async siempre retorna una Promise:
async function saludar () {
return "Hola" ;
}
// Es equivalente a:
function saludar () {
return Promise . resolve ( "Hola" );
}
// Uso:
saludar (). then ( mensaje => console . log ( mensaje )); // "Hola"
Declaraciones de Funciones Async
Función Regular
Arrow Function
Método de Objeto
Event Listener
async function miFuncion () {
return "Hola" ;
}
const miFuncion = async () => {
return "Hola" ;
};
const obj = {
async metodo () {
return "Hola" ;
}
};
button . addEventListener ( 'click' , async function () {
const datos = await obtenerDatos ();
console . log ( datos );
});
Usando Await
await pausa la ejecución de la función async hasta que la Promise se resuelva:
const asyncAwaitBtn = document . getElementById ( "async-await" );
asyncAwaitBtn . addEventListener ( "click" , async function () {
try {
console . log ( "⏳ Cargando..." );
const response = await fetch (
"https://jsonplaceholder.typicode.com/todos/1"
);
const data = await response . json ();
console . log ( data );
console . log ( "✅ Listo" );
} catch ( error ) {
console . log ( error );
}
});
Flujo de ejecución:
Se imprime ”⏳ Cargando…”
await fetch() pausa hasta que la petición termine
await response.json() pausa hasta que el JSON se parsee
Se imprime el objeto de datos
Se imprime ”✅ Listo”
Cada await pausa la función, pero no bloquea el hilo principal de JavaScript. Otras operaciones pueden ejecutarse mientras esperas.
Comparación: Promises vs Async/Await
Con Promises (.then)
function cargarUsuariosThen () {
usersOutput . textContent = "Cargando..." ;
fetch ( url )
. then ( function ( response ) {
if ( ! response . ok ) {
throw new Error ( "HTTP status " + response . status );
}
return response . json ();
})
. then ( function ( data ) {
console . log ( "Users (.then):" , data );
printUsers ( data );
})
. catch ( function ( error ) {
console . log ( error );
usersOutput . textContent = error ;
});
}
Con Async/Await
async function cargarUsuariosAsync () {
usersOutput . textContent = "Cargando..." ;
try {
var response = await fetch ( url );
if ( ! response . ok ) {
throw new Error ( "HTTP status " + response . status );
}
var data = await response . json ();
console . log ( "Users (async/await):" , data );
printUsers ( data );
} catch ( error ) {
console . log ( error );
usersOutput . textContent = error ;
}
}
Ventajas de async/await:
Más fácil de leer - parece código síncrono
Menos anidación
Manejo de errores más natural con try/catch
Mejor para debugging (call stack más claro)
Manejo de Errores con Try/Catch
Usa bloques try/catch para manejar errores en funciones async:
async function obtenerUsuario ( id ) {
try {
const response = await fetch ( `/api/users/ ${ id } ` );
if ( ! response . ok ) {
throw new Error ( `HTTP error ${ response . status } ` );
}
const usuario = await response . json ();
console . log ( 'Usuario:' , usuario );
return usuario ;
} catch ( error ) {
console . error ( 'Error al obtener usuario:' , error );
// Puedes relanzar el error o manejar la recuperación
throw error ;
}
}
Try/Catch vs .catch()
async function ejemplo () {
try {
const resultado = await operacionAsincrona ();
return resultado ;
} catch ( error ) {
console . error ( error );
}
}
Ventajas:
Sintaxis familiar
Maneja errores síncronos y asíncronos
Mejor para múltiples operaciones await
async function ejemplo () {
const resultado = await operacionAsincrona ()
. catch ( error => {
console . error ( error );
});
return resultado ;
}
Ventajas:
Más conciso para un solo await
Permite continuar después de un error
Ejecutar Promises en Paralelo
❌ Incorrecto: Secuencial (Lento)
async function obtenerTodo () {
const usuarios = await fetch ( '/api/users' ); // Espera 1 segundo
const productos = await fetch ( '/api/products' ); // Espera otro segundo
const ordenes = await fetch ( '/api/orders' ); // Espera otro segundo
// Total: 3 segundos
}
✅ Correcto: Paralelo (Rápido)
async function obtenerTodo () {
// Iniciar todas las peticiones al mismo tiempo
const promesaUsuarios = fetch ( '/api/users' );
const promesaProductos = fetch ( '/api/products' );
const promesaOrdenes = fetch ( '/api/orders' );
// Esperar a que todas terminen
const usuarios = await promesaUsuarios ;
const productos = await promesaProductos ;
const ordenes = await promesaOrdenes ;
// Total: ~1 segundo (la más lenta)
}
Usando Promise.all() con Async/Await
async function obtenerTodo () {
try {
const [ usuarios , productos , ordenes ] = await Promise . all ([
fetch ( '/api/users' ),
fetch ( '/api/products' ),
fetch ( '/api/orders' )
]);
console . log ( 'Todo cargado en paralelo' );
return { usuarios , productos , ordenes };
} catch ( error ) {
console . error ( 'Al menos una petición falló:' , error );
}
}
Si las operaciones no dependen entre sí, ejecútalas en paralelo. De lo contrario, estarás esperando innecesariamente.
Await en Ciclos
For Loop
async function procesarUsuarios ( ids ) {
for ( const id of ids ) {
const usuario = await fetch ( `/api/users/ ${ id } ` );
console . log ( 'Usuario cargado:' , usuario );
}
// Los usuarios se procesan uno por uno (secuencial)
}
Paralelo con map()
async function procesarUsuarios ( ids ) {
const promesas = ids . map ( id => fetch ( `/api/users/ ${ id } ` ));
const usuarios = await Promise . all ( promesas );
console . log ( 'Todos los usuarios cargados:' , usuarios );
// Los usuarios se cargan todos al mismo tiempo (paralelo)
}
Ejemplo Completo: Carga de Datos de Usuario
const btnUsersThen = document . getElementById ( "users-then" );
const btnUsersAsync = document . getElementById ( "users-async" );
const usersOutput = document . getElementById ( "users-output" );
const url = "https://jsonplaceholder.typicode.com/users" ;
function printUsers ( usersList ) {
var users = [];
for ( var i = 0 ; i < usersList . length ; i ++ ) {
var user = usersList [ i ];
var userDataFormatted = user . id + " - " + user . name + " (" + user . email + ")" ;
users . push ( userDataFormatted );
}
usersOutput . textContent = users . join ( " \n " );
}
async function cargarUsuariosAsync () {
usersOutput . textContent = "Cargando..." ;
try {
var response = await fetch ( url );
if ( ! response . ok ) {
throw new Error ( "HTTP status " + response . status );
}
var data = await response . json ();
console . log ( "Users (async/await):" , data );
printUsers ( data );
} catch ( error ) {
console . log ( error );
usersOutput . textContent = error ;
}
}
btnUsersAsync . addEventListener ( "click" , cargarUsuariosAsync );
Async/Await con .finally
Puedes usar .finally() con async/await:
async function cargarDatos () {
try {
mostrarSpinner ();
const datos = await fetch ( '/api/data' );
procesarDatos ( datos );
} catch ( error ) {
mostrarError ( error );
} finally {
ocultarSpinner (); // Se ejecuta siempre
}
}
O mezclarlo con Promises:
async function cargarDatos () {
mostrarSpinner ();
const datos = await fetch ( '/api/data' )
. catch ( error => {
mostrarError ( error );
return null ;
})
. finally (() => {
ocultarSpinner ();
});
if ( datos ) {
procesarDatos ( datos );
}
}
Top-Level Await
En módulos ES6 modernos, puedes usar await en el nivel superior (fuera de funciones async):
// En un módulo ES6
const datos = await fetch ( '/api/data' );
console . log ( datos );
Top-level await solo funciona en módulos ES6 (<script type="module"> o archivos .mjs en Node.js).
Buenas Prácticas
Siempre usa try/catch
// ❌ Mal: Sin manejo de errores
async function obtenerDatos () {
const data = await fetch ( '/api/data' );
return data ;
}
// ✅ Bien: Con try/catch
async function obtenerDatos () {
try {
const data = await fetch ( '/api/data' );
return data ;
} catch ( error ) {
console . error ( 'Error:' , error );
throw error ;
}
}
Ejecuta en paralelo cuando sea posible
// ❌ Lento: Secuencial
const user = await getUser ();
const posts = await getPosts ();
// ✅ Rápido: Paralelo
const [ user , posts ] = await Promise . all ([
getUser (),
getPosts ()
]);
No bloquees innecesariamente
// ❌ Mal: await innecesario
async function sumar ( a , b ) {
return await ( a + b ); // await es innecesario aquí
}
// ✅ Bien: Sin await innecesario
async function sumar ( a , b ) {
return a + b ;
}
Cuándo Usar Async/Await
Código nuevo que escribas
Múltiples operaciones asíncronas en secuencia
Cuando necesitas lógica compleja con await
Para mejor legibilidad del código
Cuando necesitas usar try/catch
Una sola operación asíncrona simple
Cuando necesitas encadenar transformaciones
APIs que esperan Promises como retorno
Cuando Promise.all/race/etc. son más claros
Próximos Pasos
Fetch API Usa async/await para hacer peticiones HTTP
Ejemplos Asíncronos Ve ejemplos completos de código asíncrono