La finalidad de los operadores async
y await
es simplificar aun más la forma en que trabajamos con las promesas, de tal forma que permite ejecutarlas y esperar el resultado de forma síncrona, si la necesidad de los famosos bloques then
y catch
.
El contenido de este artículo también lo explico por Youtube, recuerda suscribirte por que estaremos subiendo más contenido como este:
Los que ya tiene tiempo utilizando Javascript, recordaran lo complicado que era trabajar con las Callback y el famoso problema llamado callback hell, por suerte, luego apetecieron las promesas, una nueva forma de resolver el problema de asincronicidad de Javascript, permitiendo definir una serie de bloques then
y catch
que al final eran son muy parecidas a las callback, con la diferencia de que permitía tener una mejor organización del código, sin embargo, y pesar de estas mejoras significativas, Javascript seguía teniendo el mismo problema, y era la asincronicidad.
Dicho lo anterior, los operadores async/await se agregan a Javascript a partir de la versión ECMAScript 7 para simplificar la forma de trabajar con las promesas, con las cuales es posible ejecutarlas de forma síncrona y bloqueando la ejecución hasta que sean resueltas.
Para comprender mejor como funcionan los operadores async
/await
vamos a realizar un ejemplo, en el cual consumiremos un recurso de Internet mediante el API Fetch. En este primer ejemplo veremos como se hace sin async
/await
:
const fetch = require( 'node-fetch')
function getCountry(){
return fetch('https://pkgstore.datahub.io/core/country-codes/country-codes_json/data/471a2e653140ecdd7243cdcacfd66608/country-codes_json.json')
}
let hello = getCountry()
hello.then(response => response.json())
.then(response => response.map(country => country['CLDR display name']))
.then(response => console.log(response))
Cómo podemos observar, hemos creado la función getCountry
encargada de retornar una promesa con la búsqueda de un recurso de Internet, este método es ejecutado y seguido es necesario resolver la promesa con una serie de bloques then
. Podemos observar que estos es un poco verboso, pues en cada bloque hay que definir la variable response, definir una callback (o arrow function) y retornar los resultados para ser procesados por el siguiente bloque then
. Otro de los problemas es que el resultado solo estará disponible dentro del bloque then
, complicando la forma en que trabajamos y manejamos los errores.
Ahora bien, antes de explicar como funciona async
/await
quiere que veas un ejemplo, lo análisis y después pasaremos a explicar todo a detalle:
const fetch = require( 'node-fetch')
async function getCountry(){
let response = await fetch('https://pkgstore.datahub.io/core/country-codes/country-codes_json/data/471a2e653140ecdd7243cdcacfd66608/country-codes_json.json')
let json = await response.json()
return json.map(country => country['CLDR display name'])
}
(async function(){
let hello = await getCountryAsync()
console.log("log => ", hello)
})()
Para empezar, vemos un código mucho más limpio, además, la función getCountry
ejecuta todas las instrucciones de forma asíncrona. Pero que está pasado.
Lo primero que debemos de saber es que el operador await
esperará hasta que la promesa sea resuelta, lo que provocará que el hilo de ejecución hasta que la promesa se resuelva, y una vez resuelta, el hilo de ejecución continuará donde se quedo.
Otra cosa importante, es que await
solo se puede utilizar en funciones que tengan el operador async
, en caso contrario, se lanzará el siguiente error:
SyntaxError: await is only valid in async function
Los método con el operadores async
son llamados async methods, y no solo permiten utilizar el operador async
, si no que provocara que todo lo que retornemos sea encapsulado dentro de una promesa:
async function helloWorld(){
return "hello world"
}
let hello = helloWorld()
console.log(hello)
// Output
// Promise { 'hello world' }
En este ejemplo tenemos una función que solo regresa un string, sin embargo, vemos que en el output, el string "hello world"
está encapsulado dentro de una promesa.
Regresando al ejemplo, si ejecutamos la función getCountry
, este nos regresará un promesa, por lo que si imprimiéramos el resultado de la función podemos observar que nos regresa una promesa
console.log(getCountry())
// Output
// Promise { <pending> }
En este punto te podrías estar preguntando, ¿que sentido tiene utilizar await, si al final me regresará una promesa?, puede que tengas razón sin embargo, recordemos que con await
podemos esperar hasta que se resuelva una promesa, entonces podemos hacer lo siguiente:
(async function(){
let hello = await getCountry()
console.log("log => ", hello)
})
// Output
// 'Guinea-Bissau',
// 'Guyana',
// 'Haiti',
// ... 150 more items ]
En este ejemplo, vemos que hemos ejecutado la función getCountry
dentro de una async method por lo que podríamos utilizar await
para resolver la respuesta de getCountry
.
Excepciones
Otra de las grandes ventajas que ofrece el operadores async
/await
es que nos permite controlar las excepciones mediante el clásico bloque try-catch, sin necesidad de utilizar bloque .catch()
de las promesas:
async function getCountry(){
try {
let response = await fetch('https://pkgstore.datahub.io/core/country-codes/country-codes_json/data/471a2e653140ecdd7243cdcacfd66608/country-codes_json.json')
let json = await response.json()
return json.map(country => country['CLDR display name'])
} catch (err) {
console.log("Error ==> ", err)
}
}
Conclusiones
Cómo hemos podido comprobar, los operadores async
/await
proporcionan una forma mucho más imple para trabajar con promesas, permitiendo trabajar de forma síncrona.