En async/await
, que no son más que una forma de azúcar sintáctico para gestionar las promesas de una forma más similar a lo que solemos estar acostumbrados. Recordemos un fragmento de código donde manejamos promesas mediante .then()
:
fetch("/robots.txt")
.then(response => response.text())
.then(data => console.log(data));
console.log("Código síncrono.");
Con async
/await
seguimos manejando promesas, sin embargo, hay ciertos cambios importantes:
.then()
, sino que usamos un sistema más tradicional.await
Vamos a modificar el fragmento de código anterior, para manejar la fetch()
, pero en lugar de gestionarla con .then()
, lo haremos con await
. Simplemente, cualquier await
antes.
const response = await fetch("/robots.txt");
const data = await response.text();
console.log(data);
console.log("Código síncrono.");
Lo que hace
await
es detener la ejecución y no continuar. Se espera a que se resuelva la promesa, y hasta que no lo haga, no continua. A diferencia delfetch()
, tenemos un código bloqueante.
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("/robots.txt");
const data = await response.text();
return data;
}
request();
Sin embargo, aquí tenemos un problema. Estamos utilizando await
(asíncrono) dentro de request()
(síncrono), por lo que antes de ejecutarla, al intentarla definir, nos aparecerá el siguiente error:
Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules
async
Para resolver el problema anterior y poder utilizar el await
dentro de nuestra función, sólo tenemos que definir nuestra función como función asíncrona y al llamarla utilizar nuevamente el await
:
async function request() {
const response = await fetch("/robots.txt");
const data = await response.text();
return data;
}
await request();
Sin embargo, vamos a pararnos un poco a pensar esto desde las bases. Definamos dos funciones básicas exactamente iguales, ambas devuelven lo mismo, pero una es síncrona y otra asíncrona:
function sincrona() { return 42; }
async function asincrona() { return 42; }
sincrona(); // 42
asincrona(); // Promise <fulfilled>: 42
En el caso de la función sincrona()
devuelve directamente el valor, sin embargo, en el caso de la función asincrona()
devuelve una promesa que se ha cumplido inmediatamente, con el valor 42
.
Si queremos reescribirlas como arrow function, se definiría como vemos a continuación, colocando el async
justo antes de los parámetros de la arrow function:
const sincrona = () => 42;
const asincrona = async () => 42;
Recuerda que en el caso de querer controlar errores o promesas rechazadas con
async/await
, siempre podrás usar bloquestry/catch
.
En algunos casos, como al usar un fetch()
, donde tenemos que manejar dos promesas, es posible que nos interese utilizar .then()
para la primera promesa y await
para la segunda. De esta forma podemos manejarlo todo directamente, sin tener que guardarlo en constantes o variables temporales que no utilizaremos sino una sola vez:
async function request() {
return await fetch("/robots.txt")
.then(response => response.text());
}
await request();
En este caso, observa que el fetch()
devuelve una primera .then()
. La segunda response.text()
se devuelve hacia fuera y es manejada por el await
, que espera a que se cumpla, y una vez cumplida, se devuelve como valor de la función request()
.
En principio, el comportamiento de await
solo permite que se utilice en el interior de funciones declaradas como async
. Por lo que, si el ejemplo anterior lo ejecutamos en una consola de Javascript, funcionará correctamente (estamos escribiendo comandos de forma asíncrona), pero si lo escribimos en un fichero, probablemente nos aparecerá el siguiente error:
Uncaught SyntaxError:
await
is only valid inasync
function
Esto ocurre porque, como bien dice el mensaje de error, estamos ejecutando await
en el contexto global de la aplicación, y debemos ejecutarlo en un contexto de función asíncrona. Muchas veces, para solucionar esto, lo que se suele hacer es lo siguiente:
function request() {
return (async () => await fetch("/robots.txt"))();
}
Hemos incluido la función asíncrona con el await dentro de un paréntesis que la envuelve y ejecuta (función)()
, por lo que tenemos una función asíncrona autoejecutable en un contexto síncrono.
Sin embargo, actualmente esto no suele ser necesario, ya que se nos permite utilizar await
fuera de funciones async
si se encuentran en el nivel inicial de la aplicación, es decir en el ámbito global, gracias a una feature denominada top level await.
Volvamos al ejemplo que hemos visto en los anteriores capítulos. Recordemos que la función doTask()
realiza 10 lanzamientos de un dado y nos devuelve los resultados obtenidos o detiene la tarea si se obtiene un 6. La implementación de la función sufre algunos cambios, simplificándose considerablemente.
async
antes de los parámetros de la arrow function.async
se devolverá todo envuelto en una const doTask = async (iterations) => {
const numbers = [];
for (let i = 0; i < iterations; i++) {
const number = 1 + Math.floor(Math.random() * 6);
numbers.push(number);
if (number === 6) {
return {
error: true,
message: "Se ha sacado un 6"
};
}
}
return {
error: false,
value: numbers
};
}
Pero donde se introducen cambios considerables es a la hora de consumir las promesas con async
/await
. No tendríamos que utilizar .then()
, sino que podemos simplemente utilizar await
para esperar la resolución de la promesa, obteniendo el valor directamente:
const resultado = await doTask(10); // Devuelve un objeto, no una promesa
Ciertos casos no permiten usar
async/await
. Por ejemplo, una función.map()
de unde . En esos casos, se recomienda usar un for..of
o una promesa en grupo.
Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.
Puedes encontrar más sobre mi en Manz.dev