Async Await
Async Await
info/async-await
Comprar EPUB/PDF
ES
7 de febrero de 2022
Async/await
una sintaxis especial para trabajar con promesas de una forma más confortable, llamada “async/
Existe
await”. Es sorprendentemente fácil de entender y usar.
Funciones async
Comencemos con la palabra clave async . Puede ser ubicada delante de una función como aquí:
La palabra “async” ante una función significa solamente una cosa: que la función siempre devolverá una
promesa. Otros valores serán envueltos y resueltos en una promesa automáticamente.
Por ejemplo, esta función devuelve una promesa resuelta con el resultado de 1 ; Probémosla:
1 async function f() {
2 return 1;
3 }
4
5 f().then(alert); // 1
1 async function f() {
2 return Promise.resolve(1);
3 }
4
5 f().then(alert); // 1
Entonces, async se asegura de que la función devuelva una promesa, o envuelve las no promesas y las
transforma en una. Bastante simple, ¿correcto? Pero no solo eso. Hay otra palabra, await , que solo trabaja
dentro de funciones async y es muy interesante.
1 de 14 24/3/24, 17:54
Async/await https://es.javascript.info/async-await
Await
La sintaxis:
await hace que JavaScript espere hasta que la promesa responda y devuelve su resultado.
1 async function f() {
2
3 let promise = new Promise((resolve, reject) => {
4 setTimeout(() => resolve("¡Hecho!"), 1000)
5 });
6
7 let result = await promise; // espera hasta que la promesa se resuelva (*)
8
9 alert(result); // "¡Hecho!"
10 }
11
12 f();
La ejecución de la función es pausada en la línea (*) y se reanuda cuando la promesa responde, con
result volviéndose su resultado. Entonces el código arriba muestra “¡Hecho!” en un segundo.
Enfaticemos: await literalmente suspende la ejecución de la función hasta que se establezca la promesa, y
luego la reanuda con el resultado de la promesa. Eso no cuesta ningún recurso de CPU, porque el motor de
JavaScript puede hacer otros trabajos mientras tanto: ejecutar otros scripts, manejar eventos, etc.
Es simplemente una sintaxis más elegante para tener el resultado de una promesa que promise.then , es
más fácil de leer y de escribir.
1 function f() {
2 let promise = Promise.resolve(1);
3 let result = await promise; // Syntax error
4 }
Es posible que obtengamos este error si olvidamos poner async antes de una función. Como se dijo,
“await” solo funciona dentro de una función async .
2 de 14 24/3/24, 17:54
Async/await https://es.javascript.info/async-await
1 async function showAvatar() {
2
3 // leer nuestro JSON
4 let response = await fetch('/article/promise-chaining/user.json');
5 let user = await response.json();
6
7 // leer usuario github
8 let githubResponse = await fetch(`https://api.github.com/users/${user
9 let githubUser = await githubResponse.json();
10
11 // muestra el avatar
12 let img = document.createElement('img');
13 img.src = githubUser.avatar_url;
14 img.className = "promise-avatar-example";
15 document.body.append(img);
16
17 // espera 3 segundos
18 await new Promise((resolve, reject) => setTimeout(resolve, 3000));
19
20 img.remove();
21
22 return githubUser;
23 }
24
25 showAvatar();
Bien limpio y fácil de leer, ¿no es cierto? Mucho mejor que antes.
3 de 14 24/3/24, 17:54
Async/await https://es.javascript.info/async-await
Por ejemplo:
1 // asumimos que este código se ejecuta en el nivel superior dentro de un mó
2 let response = await fetch('/article/promise-chaining/user.json');
3 let user = await response.json();
4
5 console.log(user);
Si no estamos usando módulos, o necesitamos soportar navegadores antiguos, tenemos una receta
universal: envolverlos en una función async anónima.
Así:
1 (async () => {
2 let response = await fetch('/article/promise-chaining/user.json'
3 let user = await response.json();
4 ...
5 })();
4 de 14 24/3/24, 17:54
Async/await https://es.javascript.info/async-await
Aquí hay una demostración de la clase Thenable ; el await debajo acepta sus instancias:
1 class Thenable {
2 constructor(num) {
3 this.num = num;
4 }
5 then(resolve, reject) {
6 alert(resolve);
7 // resuelve con this.num*2 después de 1000ms
8 setTimeout(() => resolve(this.num * 2), 1000); // (*)
9 }
10 }
11
12 async function f() {
13 // espera durante 1 segundo, entonces el resultado se vuelve 2
14 let result = await new Thenable(1);
15 alert(result);
16 }
17
18 f();
Si await obtiene un objeto no-promesa con .then , llama tal método proveyéndole con las funciones
incorporadas resolve y reject como argumentos (exactamente como lo hace con ejecutores
Promise regulares). Entonce await espera hasta que une de ellos es llamado (en el ejemplo previo
esto pasa en la línea (*) ) y entonces procede con el resultado.
5 de 14 24/3/24, 17:54
Async/await https://es.javascript.info/async-await
1 class Waiter {
2 async wait() {
3 return await Promise.resolve(1);
4 }
5 }
6
7 new Waiter()
8 .wait()
9 .then(alert); // 1 (lo mismo que (result => alert(result)))
El significado es el mismo: Asegura que el valor devuelto es una promesa y habilita await .
Manejo de Error
Si una promesa se resuelve normalmente, entonces await promise devuelve el resultado. Pero en caso
de rechazo, dispara un error, tal como si hubiera una instrucción throw en aquella línea.
Este código:
En situaciones reales, la promesa tomará algún tiempo antes del rechazo. En tal caso habrá un retardo antes
de que await dispare un error.
Podemos atrapar tal error usando try..catch , de la misma manera que con un throw normal:
6 de 14 24/3/24, 17:54
Async/await https://es.javascript.info/async-await
1 async function f() {
2
3 try {
4 let response = await fetch('http://no-such-url');
5 } catch(err) {
6 alert(err); // TypeError: failed to fetch
7 }
8 }
9
10 f();
En el caso de un error, el control salta al bloque catch . Podemos también envolver múltiples líneas:
1 async function f() {
2
3 try {
4 let response = await fetch('/no-user-here');
5 let user = await response.json();
6 } catch(err) {
7 // atrapa errores tanto en fetch como en response.json
8 alert(err);
9 }
10 }
11
12 f();
Si no tenemos try..catch , entonces la promesa generada por el llamado de la función async f() se
vuelve rechazada. Podemos añadir .catch para manejarlo:
1 async function f() {
2 let response = await fetch('http://no-such-url');
3 }
4
5 // f() se vuelve una promesa rechazada
6 f().catch(alert); // TypeError: failed to fetch // (*)
Si olvidamos añadir .catch allí, obtendremos un error de promesa no manejado (visible en consola).
Podemos atrapar tales errores usando un manejador de evento global unhandledrejection como está
descrito en el capítulo Manejo de errores con promesas.
7 de 14 24/3/24, 17:54
Async/await https://es.javascript.info/async-await
async/await y promise.then/catch
Cuando usamos async/await , raramente necesitamos .then , porque await maneja la espera
por nosotros. Y podemos usar un try..catch normal en lugar de .catch . Esto usualmente (no
siempre) es más conveniente.
Pero en el nivel superior del código, cuando estamos fuera de cualquier función async , no estamos
sintácticamente habilitados para usar await , entonces es práctica común agregar .then/catch
para manejar el resultado final o errores que caigan a través, como en la línea (*) del ejemplo arriba.
En caso de error, se propaga como es usual, desde la promesa que falla a Promise.all , y entonces
se vuelve una excepción que podemos atrapar usando try..catch alrededor del llamado.
Resumen
El comando async antes de una función tiene dos efectos:
El comando await antes de una promesa hace que JavaScript espere hasta que la promesa responda.
Entonces:
1. Si es un error, la excepción es generada — lo mismo que si throw error fuera llamado en ese mismo
lugar.
2. De otro modo, devuelve el resultado.
Juntos proveen un excelente marco para escribir código asincrónico que es fácil de leer y escribir.
8 de 14 24/3/24, 17:54
Async/await https://es.javascript.info/async-await
Tareas
Rescribir este código de ejemplo del capítulo Encadenamiento de promesas usando async/await en vez
de .then/catch :
1 function loadJson(url) {
2 return fetch(url)
3 .then(response => {
4 if (response.status == 200) {
5 return response.json();
6 } else {
7 throw new Error(response.status);
8 }
9 });
10 }
11
12 loadJson('https://javascript.info/no-such-user.json')
13 .catch(alert); // Error: 404
solución
Las notas están bajo el código:
1 async function loadJson(url) { // (1)
2 let response = await fetch(url); // (2)
3
4 if (response.status == 200) {
5 let json = await response.json(); // (3)
6 return json;
7 }
8
9 throw new Error(response.status);
10 }
11
12 loadJson('https://javascript.info/no-such-user.json')
13 .catch(alert); // Error: 404 (4)
Notas:
1.
9 de 14 24/3/24, 17:54
Async/await https://es.javascript.info/async-await
2.
3.
Podemos devolver return response.json() en lugar de esperar por él, como esto:
1 if (response.status == 200) {
2 return response.json(); // (3)
3 }
Entonces el código externo tendría que esperar que la promesa se resuelva. En nuestro caso
eso no importa.
4.
El error arrojado por loadJson es manejado por .catch . No podemos usar await
loadJson(…) allí, porque no estamos en una función async .
Debajo puedes encontrar el ejemplo “rethrow”. Rescríbelo usando async/await en vez de .then/
catch .
10 de 14 24/3/24, 17:54
Async/await https://es.javascript.info/async-await
1 class HttpError extends Error {
2 constructor(response) {
3 super(`${response.status} for ${response.url}`);
4 this.name = 'HttpError';
5 this.response = response;
6 }
7 }
8
9 function loadJson(url) {
10 return fetch(url)
11 .then(response => {
12 if (response.status == 200) {
13 return response.json();
14 } else {
15 throw new HttpError(response);
16 }
17 });
18 }
19
20 // Pide nombres hasta que github devuelve un usuario válido
21 function demoGithubUser() {
22 let name = prompt("Ingrese un nombre:", "iliakan");
23
24 return loadJson(`https://api.github.com/users/${name}`)
25 .then(user => {
26 alert(`Nombre completo: ${user.name}.`);
27 return user;
28 })
29 .catch(err => {
30 if (err instanceof HttpError && err.response.status == 404) {
31 alert("No existe tal usuario, por favor reingrese.");
32 return demoGithubUser();
33 } else {
34 throw err;
35 }
36 });
37 }
38
39 demoGithubUser();
solución
No hay trampas aquí. Simplemente reemplaza .catch con try...catch dentro de
demoGithubUser y agrega async/await donde sea necesario:
11 de 14 24/3/24, 17:54
Async/await https://es.javascript.info/async-await
1 class HttpError extends Error {
2 constructor(response) {
3 super(`${response.status} for ${response.url}`);
4 this.name = 'HttpError';
5 this.response = response;
6 }
7 }
8
9 async function loadJson(url) {
10 let response = await fetch(url);
11 if (response.status == 200) {
12 return response.json();
13 } else {
14 throw new HttpError(response);
15 }
16 }
17
18 // Pregunta por un nombre de usuario hasta que github devuelve un usuario
19 async function demoGithubUser() {
20
21 let user;
22 while(true) {
23 let name = prompt("Ingrese un nombre:", "iliakan");
24
25 try {
26 user = await loadJson(`https://api.github.com/users/${name
27 break; // sin error, salir del bucle
28 } catch(err) {
29 if (err instanceof HttpError && err.response.status == 404
30 // bucle continúa después del alert
31 alert("No existe tal usuario, por favor reingrese.");
32 } else {
33 // error desconocido, lo relanza
34 throw err;
35 }
36 }
37 }
38
39
40 alert(`Nombre completo: ${user.name}.`);
41 return user;
42 }
43
44 demoGithubUser();
12 de 14 24/3/24, 17:54
Async/await https://es.javascript.info/async-await
Tenemos una función “regular” llamada f . ¿Cómo llamar la función async , wait() y usar su resultado
dentro de f ?
P.D. La tarea es técnicamente muy simple, pero la pregunta es muy común en desarrolladores nuevos en
async/await.
solución
Este es el caso cuando saber cómo trabaja por dentro es útil.
1 async function wait() {
2 await new Promise(resolve => setTimeout(resolve, 1000));
3
4 return 10;
5 }
6
7 function f() {
8 // muestra 10 después de 1 segundo
9 wait().then(result => alert(result));
10 }
11
12 f();
13 de 14 24/3/24, 17:54
Async/await https://es.javascript.info/async-await
Comentarios
● Si tiene sugerencias sobre qué mejorar, por favor enviar una propuesta de GitHub o una solicitud
de extracción en lugar de comentar.
● Si no puede entender algo en el artículo, por favor explique.
● Para insertar algunas palabras de código, use la etiqueta <code> , para varias líneas –
envolverlas en la etiqueta <pre> , para más de 10 líneas – utilice una entorno controlado
(sandbox) (plnkr, jsbin, codepen…)
14 de 14 24/3/24, 17:54