Exploración de funciones Async/Await en JavaScript

Introducción
Las promesas nos brindan una manera más sencilla de lidiar con la asincronía en nuestro código de manera secuencial. Teniendo en cuenta que nuestros cerebros no están diseñados para lidiar con la asincronía de manera eficiente, esta es una adición muy bienvenida. Funciones async/await , una nueva incorporación con ES2017 (ES8), nos ayuda aún más al permitirnos escribir código que parezca completamente sincrónico mientras realizamos tareas asincrónicas detrás de escena.
La funcionalidad lograda usando funciones asíncronas se puede recrear combinando promesas con generadores, pero las funciones asíncronas nos brindan lo que necesitamos sin ningún código repetitivo adicional.
Ejemplo sencillo
En el siguiente ejemplo, primero declaramos una función que devuelve una promesa que se resuelve en un valor de
después de 2 segundos. Luego declaramos unaasíncronofunción yesperarPara que la promesa se resuelva antes de registrar el mensaje en la consola:
function scaryClown() { return new Promise(resolve = { setTimeout(() = { resolve(' '); }, 2000); });}async function msg() { const msg = await scaryClown(); console.log('Message:', msg);}msg(); // Message: -- after 2 seconds
await
es un operador nuevo que se utiliza para esperar a que se resuelva o rechace una promesa. Solo se puede utilizar dentro de una función asincrónica.
El poder de las funciones asincrónicas se hace más evidente cuando hay varios pasos involucrados:
function who() { return new Promise(resolve = { setTimeout(() = { resolve(' '); }, 200); });}function what() { return new Promise(resolve = { setTimeout(() = { resolve('lurks'); }, 300); });}function where() { return new Promise(resolve = { setTimeout(() = { resolve('in the shadows'); }, 500); });}async function msg() { const a = await who(); const b = await what(); const c = await where(); console.log(`${ a } ${ b } ${ c }`);}msg(); // lurks in the shadows -- after 1 second
Sin embargo, tenga cuidado: en el ejemplo anterior, cada paso se realiza de forma secuencial y cada paso adicional espera a que el paso anterior se resuelva o se rechace antes de continuar. Si, en cambio, desea que los pasos se realicen en paralelo, puede simplemente usarPromesa.todoesperar que todas las promesas se cumplan:
// ...async function msg() { const [a, b, c] = await Promise.all([who(), what(), where()]); console.log(`${ a } ${ b } ${ c }`);}msg(); // lurks in the shadows -- after 500ms
Promesa.tododevuelve una matriz con los valores resueltos una vez que se han resuelto todas las promesas pasadas.
En lo anterior también hacemos uso de una agradable desestructuración de matrices para que nuestro código sea conciso.
Promesa de retorno
Las funciones asincrónicas siempre devuelven una promesa, por lo que lo siguiente puede no producir el resultado que busca:
async function hello() { return 'Hello Alligator!';}const b = hello();console.log(b); // [object Promise] { ... }
Dado que lo que se devuelve es una promesa, podrías hacer algo como esto:
async function hello() { return 'Hello Alligator!';}const b = hello();b.then(x = console.log(x)); // Hello Alligator!
…o simplemente esto:
async function hello() { return 'Hello Alligator!';}hello().then(x = console.log(x)); // Hello Alligator!
Diferentes formas
Hasta ahora con nuestros ejemplos vimos la función async como una declaración de función, pero también podemos definir expresiones de función async y funciones de flecha async:
Expresión de función asíncrona
Aquí está la función async de nuestro primer ejemplo, pero definida como una expresión de función:
const msg = async function() { const msg = await scaryClown(); console.log('Message:', msg);}
Función de flecha asincrónica
Aquí está el mismo ejemplo una vez más, pero esta vez definido como una función de flecha:
const msg = async () = { const msg = await scaryClown(); console.log('Message:', msg);}
Manejo de errores
Otra cosa muy buena de las funciones asincrónicas es que el manejo de errores también se realiza de forma completamente sincrónica, mediante las buenas y antiguas sentencias try…catch . Demostremos esto mediante una promesa que se rechazará la mitad de las veces:
function yayOrNay() { return new Promise((resolve, reject) = { const val = Math.round(Math.random() * 1); // 0 or 1, at random val ? resolve('Lucky!!') : reject('Nope '); });}async function msg() { try { const msg = await yayOrNay(); console.log(msg); } catch(err) { console.log(err); }}msg(); // Lucky!!msg(); // Lucky!!msg(); // Lucky!!msg(); // Nope msg(); // Lucky!!msg(); // Nope msg(); // Nope msg(); // Nope msg(); // Nope msg(); // Lucky!!
Dado que las funciones asincrónicas siempre devuelven una promesa, también puedes lidiar con errores no controlados como lo harías normalmente usando una declaración catch:
async function msg() { const msg = await yayOrNay(); console.log(msg);}msg().catch(x = console.log(x));
Este manejo de errores sincrónico no solo funciona cuando se rechaza una promesa, sino también cuando se produce un error de sintaxis o de tiempo de ejecución real. En el siguiente ejemplo, la segunda vez que llamamos a nuestromensajefunción que pasamos en unanúmerovalor que no tieneaMayúsculasmétodo en su cadena de prototipos. Nuestro bloque try…catch detecta ese error también:
function caserUpper(val) { return new Promise((resolve, reject) = { resolve(val.toUpperCase()); });}async function msg(x) { try { const msg = await caserUpper(x); console.log(msg); } catch(err) { console.log('Ohh no:', err.message); }}msg('Hello'); // HELLOmsg(34); // Ohh no: val.toUpperCase is not a function
Funciones asincrónicas con API basadas en promesas
Como mostramos en nuestra introducción a la API Fetch, las API web basadas en promesas son candidatas perfectas para funciones asincrónicas:
async function fetchUsers(endpoint) { const res = await fetch(endpoint); let data = await res.json(); data = data.map(user = user.username); console.log(data);}fetchUsers('https://jsonplaceholder.typicode.com/users');// ["Bret", "Antonette", "Samantha", "Karianne", "Kamren", "Leopoldo_Corkery", "Elwyn.Skiles", "Maxime_Nienow", "Delphine", "Moriah.Stanton"]
Compatibilidad del navegador: a partir de 2020, el 94 % de los navegadores en todo el mundo pueden manejar async/await en javascript. Las excepciones notables son IE11 y Opera Mini.
Conclusión
Antes de las funciones Async/await, el código JavaScript que dependía de muchos eventos asincrónicos (por ejemplo: código que realizaba muchas llamadas a API) terminaba en lo que algunos llamaban “el infierno de las devoluciones de llamadas”: una cadena de funciones y devoluciones de llamadas que era muy difícil de leer y comprender.
Async y await nos permiten escribir código JavaScript asincrónico que se lee mucho más claramente.
Deja una respuesta