Si ya conoces las promesas en Javascript y las bases del async/await, quizás te pueda interesar utilizar promesas mediante async
/await
, ya que es una sintaxis alternativa mucho más simple y clara de escribir.
La palabra clave await
La palabra clave await
se coloca justo antes de una promesa para «esperar a que se resuelva». Vamos a modificar el fragmento de código anterior, para manejar la fetch()
, pero en lugar de gestionarla con .then()
, lo haremos con await
.
const response = await fetch("/data.json");
const data = await response.json();
console.log(data);
console.log("Código síncrono.");
Lo que hace await
es «pausar» la función. Se esperará a que se resuelva la promesa, y hasta que no lo haga, no continua. A diferencia del .then()
, el código parece ser bloqueante, aunque tiene matices que explicaremos más adelante.
Ahora, vamos a introducir este fragmento de código dentro de una función llamada request()
. Quedaría de la siguiente forma:
function request() {
const response = await fetch("/data.json");
const data = await response.json();
return data;
}
const data = request();
Sin embargo, aquí tenemos un problema. Estamos utilizando await
(asíncrono) dentro de una función request()
(síncrono), por lo que antes de ejecutarla, al intentarla definir, nos aparecerá un error similar al siguiente:
Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules
Para evitar este error, debemos convertir la función request()
en asíncrona.
La palabra clave async
Para poder utilizar el await
dentro de nuestra función, tenemos que asegurarnos de que nuestra función es asíncrona, algo que se soluciona simplemente añadiéndole un async
antes de definirla. En el caso de las arrow function, antes de los parámetros:
// Con funciones tradicionales
async function request() {
const response = await fetch("/data.json");
const data = await response.json();
return data;
}
await request();
// Con funciones arrow (flecha)
const request = async () => {
const response = await fetch("/data.json");
const data = await response.json();
return data;
}
await request();
Importante: Ten en cuenta que al añadir la palabra clave
async
a una función, la convertimos en asíncrona, por lo que a su vez, está devolviendo su propia promesa. Si quieres obtener el valor final, utiliza nuevamenteawait
al llamar arequest()
.
Promesas rechazadas con try/catch
En anteriores temas vimos que podíamos utilizar el método .catch()
después de los .then()
para gestionar cuando una promesa se rechazaba. Sin embargo, en el caso de querer controlar errores o promesas rechazadas con async/await
, tendríamos que usar un bloque try/catch
:
const request = async () => {
try {
const response = await fetch("/data.json");
const data = await response.json();
return data;
} catch (error) {
console.error("Error al realizar la petición o procesar los datos: ", error);
}
}
Todo el código en el interior del bloque try
, estará siendo examinado, de modo que si ocurre un error o se rechaza una promesa, se interrumpe la ejecución y se salta al bloque catch
.
De esta forma, los bloques try
/catch
se están encargando de controlar los errores o el rechazo de promesas, tal y como hacíamos con el método .catch()
en los thennables.
Async/await + .then()
Es importante darse cuenta que no es necesario utilizar una misma estrategia siempre, sino que se pueden combinar. Al usar un fetch()
, donde tenemos que manejar dos promesas encadenadas, es posible utilizar una estrategia hibrida donde utilicemos .then()
para la primera promesa y await
para la segunda, por ejemplo:
async function request() {
return await fetch("/data.json")
.then(response => response.json());
}
await request();
function request() {
return fetch("/data.json")
.then(async response => await response.json());
}
await request();
En este caso, observa que el fetch()
devuelve una primera .then()
. La segunda response.json()
se devuelve hacia fuera del fetch()
y es manejada por el await
, que espera a que se cumpla, y una vez resuelta, se devuelve como valor de la función request()
.
También se podría hacer a la inversa, como en el segundo ejemplo. En este caso, al final hacemos un await request()
no porque la función sea estrictamente asíncrona (que no lo es), sino porque el fetch()
devuelve una promesa, y por lo tanto usamos el await
para esperar a que se cumpla.
Importante: Ciertos casos no permiten usar
async/await
, como por ejemplo el método.map()
de unde . En esos casos, se recomienda usar un for..of
o controlar las promesas.