¿Alguna vez te has preguntado el por qué existe async await si ya se tiene las promesas? Muchos desarrolladores ya están usando esta funcionalidad. Sin embargo, no saben cómo funciona o cómo es diferente de las promesas. Eso es exactamente lo que abarcará este post. Empezaremos recordando por qué llegaron las promesas y cómo es que funciona, luego será el turno de async await y su funcionamiento. Finalmente veremos un ejemplo de cómo aplicar estas dos formas de manejar la asíncronia en conjunto.
Para poder entender varios términos que se va a usar, es necesario tener cierto conocimiento previo de Javascript y su asíncronia. Por eso, te recomiendo leer estos posts. Son muy buenos!
Ya tenemos todo lo necesario y ya estás listo para seguir leyendo este post. Esta es una oportunidad de entender mejor la asíncronia en Javascript. A por ello!
Las promesas llegan en ECMAscript 2016 como solución a uno de los problemas que generaba los callbacks, el callback hell. Este hacía que el código asíncronico se apile horizontalmente a la derecha. Por lo cual esto hacía que el código se vuelva muy complicado de leer. Lo que planteaba las promesas era una mejor sintaxis. En vez de que el código esté horizonal, pues que esté vertical y encadenado. Veamos un ejemplo para entenderlo mejor.
En estos trozos de código hacemos unas llamadas a un api para conseguir el id de un usuario, conseguir sus seguidores y haces más cosas. Haciendo esto con tan solo callbacks, nos resulta como en la imagen. Imagínate que haya muchos más callbacks. ¿Difícil de leer, no?
callEndpoint('api/getidbyusername/davos', function (results) {
callEndpoint('api/getfollowersbyid' + results.userId, function (results) {
callEndpoint('api/someothercall', function (results) {
callEndpoint('api/someothercall', function (results) {
callEndpoint('api/someothercall', function (results) {
callEndpoint('api/someothercall', function (results) {
// do things here
})
})
})
})
})
})
Pero con las promesas todo es diferente, el código se vuelve vertical y más legible.
callEndpoint('api/getidbyusername/davos')
.then(results => callEndpoint('api/getfollowersbyid' + results.userId))
.then(results => callEndpoint('api/someothercall'))
.then(results => callEndpoint('api/someothercall'))
.then(results => callEndpoint('api/someothercall'))
.then(results => callEndpoint('api/someothercall'))
Entonces, ¿Las promesas es solo sintaxis y funcionan igual que los callbacks?
No exactamente. Es cierto que las promesas manejan código que será ejecutado en algún futuro al igual que los callbacks. Nótese aquí la incertidumbre de cuándo será ejecutado este código. Sin embargo, la diferencia está en el mecanismo de las promesas. Pero antes, repasemos un poco para entender este mecanismo. El código síncrono inmediatamente se va a un lugar llamado el Call Stack, aquí la última función que entra al stack es el primero que se ejecuta y que sale del stack así hasta la primera que ingreso. Por otro lado, el asíncrono se va a una cola de tareas para su respectiva ejecución. Una vez que el Call Stack esté vacío, el Event Loop moverá las funciones que ya estén listas de la cola de tareas al Call Stack y luego pasarán a mostrar su resultado. Con esto en mente retomemos las promesas. Estas se dirigen a una cola de tareas diferente a las que van los callbacks. Los callbacks se van al Task Queue y las promesas al PromiseJobs o también llamado MicroTask Queue. Estos son manejadores de tareas, básicamente son los que deciden qué funciones son las que entran y las que salen.
Si te has confundido o si aún así quieres saber un poco más de los Tasks, MicroTasks y queues, te dejo este post muy bueno para profundizar estos conceptos.
Tasks, microtasks, queues and schedules
Ahora ya tenemos una idea de cómo funciona las promesas. ¿Y async await? Pues vamos a ello.
En ECMAscript 2017 es cuando Async Await entra al juego. Este nuevo feature de Javascript planteaba un mejor manejo de las promesas. Estos ya no estarían encadenados uno del otro volviendo la sintaxis más entendible y fácil de usar. Sobre todo fácil de usar. Para usarlo tan solo se necesita async functions y la keyword await. Este keyword permite que una promesa se resuelva y retorne su valor, esto permite que podamos guardarlo en variables. Pero no todo podía ser oro. await solo funciona en async functions. Este tipo de funciones simplemente se aseguran que lo que sea que retornen sea una promesa. Dicho de otro modo, estas funciones siempre retornan una promesa. Veámoslo en un ejemplo.
Tomaremos el ejemplo de las promesas y convertiremos su sintaxis usando async await
// usando Promesas
callEndpoint('api/getidbyusername/davos')
.then(results => callEndpoint('api/getfollowersbyid' + results.userId))
.then(results => callEndpoint('api/someothercall'))
.then(results => callEndpoint('api/someothercall'))
.then(results => callEndpoint('api/someothercall'))
.then(results => callEndpoint('api/someothercall'))
// usando Async AwaitasyncfunctioncallEndpoints() {
const userResults = await callEndpoint('api/getidbyusername/davos')
const followersResults = await callEndpoint('api/getfollowersbyid' + userResults.userId)
const someResults = await callEndpoint('api/someothercall')
const moreResults = await callEndpoint('api/someothercall')
const anotherResults = await callEndpoint('api/someothercall')
const finalResults = await callEndpoint('api/someothercall')
return finalResults
}
callEndpoints()
Después de ver la sintaxis creo que estamos de acuerdo que es mucho más simple y entendible de usar. Sin embargo, el manejo de async await es diferente a la de las promesas. Sabemos que await hace una pausa hasta que la promesa se resuelva. Literalmente, hace que la ejecución del async function espere hasta que la promesa se resuelva y retorne un valor, aúnque esto no detiene el engine del lenguaje, este aún puede ejecutar otros scripts o eventos, esto significa que está volviendo el código asíncrono en síncrono. Y tal vez habrás pensado en qué sentido tiene esto si ya no va a ser asíncrono o no sea de utilidad y que mejor sigues trabajando con las promesas. Pues, esto no es totalmente cierto. Async Await puede brillar en ciertos casos de uso donde necesitemos esperar y saber cuándo alguna función asíncrona se ejecute, por ejemplo en el caso de pedidos a una api, donde necesitemos que primero la página se llene de datos para que el usuario pueda interactuar.
Pero, y si te dijera que podemos ir aún más allá y combinar lo mejor de ambos mundos. Podemos aprovechar la pausa de async await y las utilidades de las promesas como Promise.all
. Esto lo veremos en el siguiente tema en un caso de uso donde sean necesario tener ambas.
Vamos a suponer que estamos programando una carga inicial del perfil de usuario y que uno de los requerimientos sea que debamos mostrar la información básica del usuario, los cursos que ha tomado en la plataforma y la lista de sus amigos antes de que termine la carga. Estos recursos se consiguen por medio de una api, y cada recurso está en una diferente url. Y las url del api de los cursos y la de amigos vienen en la información del usuario en la propiedad links.
Este es un ejemplo de la respuesta a un pedido a la url de la información del usuario
{
user: {
id: 1234,
...
links: ['api/user/1234/courses', 'api/user/1234/friends']
}
}
Entonces tenemos que hacer 3 pedidos a la api y debemos tener acceso a su data antes de que termine la carga. Está claro lo que debemos usar, async await y promesas.
Vamos a crear una async function donde en primera instancia haremos un pedido a la url del usuario para obtener la información básica y los links que están como propiedad del usuario. Luego, usaremos una utilidad de las promesas, Promise.all
. Esto hará que los pedidos se ejecuten paralelamente, por lo tanto el tiempo de espera se disminuye al no tener que ejectuar los pedidos de los links consecutivamente. Un detalle es que si alguno de estos pedidos falla en el Promise.all
, todos fallarán. O todo o nada.
Dentro de Promise.all
, iteraremos sobre los links con la función de los arreglos, map
. Este recibe una función que tiene como argumento el elemento del arreglo en cada iteración, en este caso el link. Luego dentro de la función, aplicamos un arrow function que retorna un fetch al link en cada iteración. Esto hará que en cada iteración se retorne una promesa. Al final, tendremos un arreglo de estas promesas sin resolver. Para esto aplicamos await al Promise.all
para que resuelva todas las promesas paralelamente. Una vez ya resueltos, obtendremos todas las respuestas de los pedidos si todo fue bien y lo guardamos en una variable userResponse
. Por último aplicamos todo esto de nuevo para parsear las respuestas en data de tipo objeto para que Javascript pueda hacer operaciones sobre la data.
asyncfunctiongetAllUserInfo(id) {
const user = await fetch('api/user/' + id)
const userResponse = awaitPromise.all(user.links.map(link => fetch(link)))
const userData = awaitPromise.all(userResponse.map(response => response.json()))
return userData
}
Por último obtenemos la data requerida para el usuario, y logramos hacer que la información se visualice por el usuario final.
Para resumir, las promesas y async await resuelven la asincronía de distinta forma. Con las promesas no sabemos cuándo se va a resolver y con async await forzamos una espera en la función. No siempre se va a usar uno, el otro o ambos, por ello lo primero es entender el caso de uso y después empezamos a implementar todo lo que hemos aprendido aquí.
Buen post recopilando todo, sencillo de entender y con una aplicación directa a cada cosa
Gracias por el feedback!
Tendré que volver luego de ver unos cuantos videos mas en YouTube porque me esta costando un poco entender cuando se usan al mismo tiempo, trabajando juntos.
gracias por el aporte, entiendo mejor acerca del uso de las api’s y la necesidad de usar asincronía en nuestros proyectos que muchas veces son síncronos, pues, buen punto, muchas gracias, Dios los bendiga Platzi.
Gracias por el aporte, aun no me queda claro del todo pero espero que con la practica pueda tomarle el sentido…
Muchas gracias por compartir
Me alegro que te sirva, Pamela. 😃
Uff que gran post, muchas gracias bro.
Excelente tu post!!
Gracias!
Este post me vino de 10! muy buen aporte! muchas gracias!!
genial
No entiendo muy bien cual es la utilidad de que se ejecuten de manera paralela si la entrada de uno es la salida de otro
Gracias
Excelente Post. Gracias.
usi que he disfrutado este platzi day saludos gracias
Excelente aporte!!
bue post
Excelente post, sirve mucho para entender la diferencia.
Muy buen post, lo agregó a mis apuntes. Gracias!
muy buen aporte
Muchas gracias 😃
Veo este aporte de nuevo, porque es excelente!
Muchas gracias por compartir este post, me aclaró algunas dudas
Está Genial !! muchas gracias
excelente
Muy buen post!
Estoy haciendo el curso de fundamentos de JS y me vine a este post para entender de manera mas clara sobre async await. Muchas gracias por ponerlo en palabras tan claras y de fácil comprensión.
Gracias por este gran post!, de gran utilidad.
Muchas gracias me has hecho entender mejor, las promesas y las async await
fue de gran ayuda, gracias!!
Interesante, me aclaró algunas dudas, pero debo seguir profundizando más en este tema.
Es excelente el post, realmente jamás me había puesto a pensar sobre todo esto, pero es algo que sirve de mucho.
Muy buena explicacion!
Buen post gracias!!
Gracias, me ayudo a entender un poco más.
Me encantó como pones las diferencias entre los tres, citando referencias. La verdad me ayuda mucho a tener en mi cabeza la diferencia específica. Gracias!!
Excelete Post
#Excelente!
#Genial!
Excelente el post, interesante, conciso y directo.
Increíble post, algo así estuve buscando para comprender como trabaja Async await y de que forma los interpreta el Engine.
Que buena explicación, gracias.
Uno de los mejores y más completos posts que he visto en platzi. Infinitas gracias por esta gran contribución!
Excelente que buena información, lo mejor de todo es que fue con un ejemplo real, lo cual ayuda a dejar el concepto mucho mas claro
Un aporte, de altos niveles… ¡Gracias!
guardado en favoritos, gracias!
Hola,
Esta Re_bien la explicación, ahora queda practicar!
Gracias.
¡Excelente aporte!
Muchas gracias!. Si que me ayudo a entender mejor este tema.
Me huele a que tendría mucha utilidad en formularios con mucho UX donde una pregunta no pueda ser ejecutada si no se resuelve otra antes, y en exámenes virtuales, como los de Platzi. Diganme si estoy por ahi en lo cierto. o No? 😦 jaja