JavaScript — ES8 (ECMA2017)-Parte III / async-await
Temario
- Introducción
- Nuevas Características
• async
• await
i. Introducción
Esta story es la continuación de:
- ES8 — ES2017 (I[ref], II [ref])
- ES7 — ES2016[ref]
- ES6 — ES2015 (I[ref], II[ref],III[ref],IV[ref],V[ref])
- JavaScript — Historia, Estándar & Motores [ref]
Vamos a ver la tercera parte de novedades que nos trae la edición ES8 (ES2017) de JavaScript.
Importante: Hay que tener en cuenta que es posible que NO TODOS los navegadores modernos admitan estas “nuevas” funcionalidades, por lo que te sugiero primero validar las versiones de los mismos antes de subir a producción. Si estas usando algún transpilador (ej. Babel) no deberás preocuparte.
Antes de comenzar te sugiero que primero leas:
ii. Nuevas Características
01. async/await [ref]/[ref]
Cuando introdujeron las promesas en ES6 el objetivo principal fue resolver el problema asíncrono (lo cual lograron), además de “resolver” el callback hell, aunque seamos sinceros agregaron complejidad que quizás en su momento no estábamos preparados (y ellos lo sabían) por lo que en la edición ES8 nos presentan async/await
.
Una función asíncrona es una función declarada con la palabra clave
async
, donde nos va a permitir tener un comportamiento asíncrono basado en promesas con un estilo “más limpio” y sin bloquear el hilo de ejecución.
Las funciones asíncronas son una combinación entre promesas y generadores, digamos que son una abstracción de mayor nivel sobre las promesas, ya que async/await
están basados en promesas y una de sus ventajas es que hace que el código parezca síncrono.
Síncrono: Es cuando se ejecuta una operación de entrada/salida de manera secuencial es decir, se debe esperar a que se complete para poder procesar el resultado.
Sintaxis —
Donde:
name
[obligatorio] — Nombre de la función (también puede ser una función de expresión)param
[opcional] — Los argumentos que se le pasan a la funciónstatements
[obligatorio] — La lógica de la función, se sugiere que siempre tenga unreturn
aunque no es obligatorio
Ejemplo 1 —
Podemos observar que en la línea 5 nos retorna una promesa completada, al ser considerada una promesa, debemos tratarla como tal entonces si queremos retornar el valor:
Lo interesante es que podemos tratar el return
con Promise.resolve
:
Podemos observar que tiene un comportamiento similar a una promesa es decir envuelto en
Promise.resolve
, PERO es importante saber que una promesa y una función asíncrona NO son equivalentes ya que ambas regresan referencias diferentes.
Para hacer mas orgánico la espera de una función asíncrona se utiliza el operador await
Donde:
[rv]
[opcional] — Valor cumplido de la promesaPromise
[obligatorio] — Cualquier promesa a esperar
Importante: El operador
await
solo se puede usar dentro de una función asíncrona y NO fuera de ella (por ahora 😉) , así como tampoco puedes usarla dentro de una función regular ya que de lo contrario lanzará unSyntaxError
Cuando se desea llamar una función asíncrona solo se debe anteponer await
, el código de llamada se detendrá hasta que la función retorne un resultado.
Ejemplo 2—
Podemos observar que se ha cambiado el .then
por await
, es decir cada que ocupamos await
este automáticamente se encargará de extraer el valor resuelto, por lo que ya no es necesario utilizar then
El mismo ejemplo con Promise.resolve
Ejemplo 3—
Hagamos un ejemplo un poco mas complejo, y siguiendo las reglas del await
Podemos observar que:
- En la línea 2 se esta creando una promesa para que sea resuelta en 2 seg.
- En la línea 6 por medio de
await
se invocamyPromise
donde va a esperar a que se resuelva, una vez resuelto, asigna la promesa resuelta a la variablemessage
- Finalmente en la línea 8 mostramos el resultado
Ejemplo 4—
Veamos primero un ejemplo con fetch
y then
, donde vamos a consumir la api de themoviedb
[ref]:
Ahora ese mismo ejemplo, con async/await
:
Podemos observar en la animación de arriba que:
- En la línea 12 hace la petición al servicio por medio del
fetch
, y con elawait
va a esperar a que se resuelva la petición, para almacenar el valor en la variableresponse
- En la línea 13 espera a que la respuesta se convierta en un JSON, una vez que se resuelve almacena el resultado en la variable
movie
- Finalmente en la línea 14 retornamos en consola la variable
movie
Manejo de errores —
Hasta este momento sabemos cómo ocupar async/await
siempre y cuando la promesa sea resuelta, pero… ¿qué pasa cuando la promesa no se resuelve?
Para lidiar con los errores, tenemos dos formas de hacerlo:
- El método
catch()
— Al tratarse de una promesa este método nos va a permitir manejar y tratar el error cuando es rechazado.
- La declaración
try...catch
— Usandotry
para envolver el código ycatch
para manejar cualquier error que ocurra, es decir no solo el error de la promesa.
Desventajas de usar async/await
—
Sabemos que await
pausa la ejecución de la función asíncrona en la que se encuentra (que es el comportamiento esperado), esto quiere decir que no podemos terminar su ejecución hasta que la promesa termine (resuelva o rechace).
Ejemplo 5—
Veamos el siguiente ejemplo:
Podemos observar que:
- En la línea 1, tenemos una función que se va a encargar promesas falsas
- En la línea 10 creamos un timestamp cuando la función es invocada
- En la línea 11 creamos una promesa, donde
await
va a esperar a que se resuelva, esta tardara 1000ms - En la línea 12 creamos una promesa, donde
await
va a esperar a que se resuelva, esta tardara 800ms - En la línea 13 creamos una promesa, donde
await
va a esperar a que se resuelva, esta tardara 3000ms - En la línea 15 creamos un timestamp cuando todas las promesas se han resuelto
- En la línea 17 retornamos un mensaje con el tiempo que se ha tardado, que en este caso ha sido de 4.8s
Es importante entender que anteponer await
a las promesas serán ejecutadas en secuencia (en cadena), es decir hasta que no se cumpla una, NO pasará a la siguiente.
Si quieres ejecutar todas las promesas en paralelo, deberás utilizar el método Promise.all()
(JavaScript — Callbacks y Promises[ref])
Ejemplo Final —
El siguiente es un ejemplo digamos… “complejo”, lo pongo entre comillas, ya que si tú has leído y comprendido las stories que te recomendé al principio [ref][ref][ref], entonces será un ejemplo sencillo. Veamos:
Sin ejecutar el código, ¿podrás decirme en que orden se ejecuta el código?, es decir, ¿cuál será el orden de salida de la consola?…
Si ejecutamos el código, la salida en consola es la siguiente:
Lo que esta pasando es lo siguiente:
Vamos paso a paso (haz clic en la imagen para agrandar):
No olvidemos que… —
- La palabra
await
solo pueda usarse dentro de una función asíncrona, es decir una función que tenga antepuesta la palabraasync
, de no tenerla nos dará unSyntaxError
- Las funciones asíncronas también pueden definirse como expresiones.[ref]
await
no puede ser finalizada hasta que se liquide la promesa.- JavaScript es un lenguaje de programación síncrono de un solo hilo
- Depurar es más sencillo ya que la consola lo interpretará como un código síncrono.
- Las funciones con
async
siempre devuelven una promesa. - Utilizar
try...catch
para el manejo de errores
Bibliografía y links que te puede interesar…