Javascript – Async/Await

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.

Aplicaciones reactivas con React, NodeJS & MongoDB
¿Quieres aprender las tecnologías más importantes de la web? te invito a que veas mi libro donde aprenderás React, NodeJS y MongoDB y conviértete en un desarrollador FullStack de JavaScript

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.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *