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
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
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
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:
// 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:
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
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
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
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
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
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
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:
- Hace que el código asíncrono sea más legible y mantenible
- Se basa en Promise, compatible con el código Promise existente
- Proporciona una forma más intuitiva de manejo de errores
- 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