331

Las promesas y async await lógicamente no son iguales. Y te explico el porqué.

David
davos_
29379

¿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.

  • ¿Por qué Promesas?
  • La llegada de Async Await
  • Async Await y Promesas trabajando juntos

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!

Alt Text

¿Por qué Promesas?

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.

https://miro.medium.com/max/771/1*CUyECMl99NBj7PurcEUwVg.jpeg

Referencia: https://medium.com/@jitubutwal144/javascript-how-is-callback-execution-strategy-for-promises-different-than-dom-events-callback-73c0e9e203b1

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.

La llegada de Async Await

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.

Async Await y Promesas trabajando juntos

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.

  • Información del usuario: api/user/1234
  • Cursos que ha tomado: api/user/1234/courses
  • Lista de sus amigos: api/user/1234/friends

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.

Conclusión

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í.

Escribe tu comentario
+ 2
Ordenar por:
10
9535Puntos

Buen post recopilando todo, sencillo de entender y con una aplicación directa a cada cosa

3
29379Puntos
2 años

Gracias por el feedback!

3
49300Puntos

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.

2
4892Puntos

Muchas gracias por compartir

1
29379Puntos
2 años

Me alegro que te sirva, Pamela. 😃

2
19659Puntos

Uff que gran post, muchas gracias bro.

2
18167Puntos

Excelente tu post!!

1
29379Puntos
2 años

Gracias!

1
5922Puntos

usi que he disfrutado este platzi day saludos gracias

1
5145Puntos

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.

1
1522Puntos

Excelente aporte!!

1
2952Puntos

Excelente post, sirve mucho para entender la diferencia.

1
5973Puntos

Muy buen post, lo agregó a mis apuntes. Gracias!

1
23143Puntos

Veo este aporte de nuevo, porque es excelente!

1
7658Puntos

Muchas gracias por compartir este post, me aclaró algunas dudas

1
5108Puntos

Está Genial !! muchas gracias

1
3733Puntos

excelente

1
10762Puntos

Muy buen post!

1
11374Puntos

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.

1
22070Puntos

Gracias por este gran post!, de gran utilidad.

1
7491Puntos

Muchas gracias me has hecho entender mejor, las promesas y las async await

1
17848Puntos

Interesante, me aclaró algunas dudas, pero debo seguir profundizando más en este tema.

1

Es excelente el post, realmente jamás me había puesto a pensar sobre todo esto, pero es algo que sirve de mucho.

1
61122Puntos

Gracias, me ayudo a entender un poco más.

1
6940Puntos

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!!

1
13866Puntos

Excelente el post, interesante, conciso y directo.

1
23904Puntos

Increíble post, algo así estuve buscando para comprender como trabaja Async await y de que forma los interpreta el Engine.

1
15374Puntos

Uno de los mejores y más completos posts que he visto en platzi. Infinitas gracias por esta gran contribución!

0

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

0
23049Puntos

Hola,

Esta Re_bien la explicación, ahora queda practicar!

Gracias.

0
7737Puntos

Muchas gracias!. Si que me ayudo a entender mejor este tema.

0
6855Puntos

Gracias por el aporte, aun no me queda claro del todo pero espero que con la practica pueda tomarle el sentido…

0
47949Puntos

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