Cookies

Esta web utiliza cookies para obtener datos estadísticos de la navegación de sus usuarios. Si continúas navegando consideramos que aceptas su uso.

Promesas con async/await y carga concurrente de recursos

En este mismo blog he hablado de la potencia de las promesas para manejar la comunicación cliente servidor de manera asíncrona en Javascript o, resumiendo, cualquier operación de entrada/salida. Como con cualquier poder viene la responsabilidad de gestionarlo correctamente, veremos en este post cómo configurar Webpack + Babel para poder utilizar async/await en nuestro proyecto Javascript para tener compatibilidad máxima y así utilizar esta sintaxis que nos ayudará a usar las promesas de una manera mucho más entendible.

Configurando el proyecto

Instalemos pues todo lo necesario para empezar el proyecto. Nuestro caso de uso va a ser un proyecto en cliente para cargar un listado de repositorios o información adicional a través de la API REST de Github. La solución completa está en este repositorio, eso sí, en el post veremos paso por paso como llegar a esta solución.

Veamos pues como configurar nuestro proyecto para usar Webpack + Babel, con el fin de usar la funcionalidad async/await. El primer paso es ejecutar este comando. Usaré yarn, aunque también se podría hacer con NPM.

Ejecutando este comando, tendremos un archivo package.json con lo mínimo para usar Webpack + Babel con promesas. Como nuestros ejemplos usarán React.js, también será necesario instalar las siguientes dependencias.

Para facilitar las cosas, el package.json final que debemos tener debería tener un contenido similar al siguiente.

Finalmente deberemos escribir en un fichero .babelrc el siguiente contenido.

Un ejemplo simple

Veamos como podemos cargar un listado de repositorios desde la API de Github sin despeinarnos usando async/await. Usaremos la función fetch que nos devuelve una promesa y manejaremos la respuesta que nos devuelva la API de Github. Si devuelve un código HTTP correcto, devolveremos los datos devueltos directamente, sino, lanzaremos una excepción informando del error.

En el código podemos ver como encapsulamos en la clase Client la lógica para tomar nuestros datos y desde el componente ListReposApp los cargamos en la función componentDidMount para devolver una representación de estos en la función renderRepos. No vamos a entrar en detalles respecto al ciclo de vida de los componentes React, ya que eso daría para un post entero, pero sí vamos a tratar el uso de async en la función componentDidMount. async nos permite utilizar en el cuerpo de la función la palabra clave await. Con await lo que hacemos es algo así como convertir el código asíncrono a síncrono, aunque esto sería una explicación bastante simplona.

Con await, el código que viene tras la instrucción a la que se aplica la palabra clave no se ejecutará hasta que la operación asíncrona haya finalizado, en este caso la petición HTTP. Es como si el “compilador” (Babel en este caso) generara él mismo un callback con todo el código que continúa a la ejecución de await y otro con el manejo de un posible error.

async/await nos servirá como una manera de hacer un código más entendible y que escala mejor ante callbacks anidados, aunque también comentaremos algunos casos en los que, aunque sea más fácil entender el código, async/await no sería la manera más adecuada de cargar información remota, siempre y cuando el tiempo de carga sea una de nuestras prioridades.

await y la carga de N recursos de manera concurrente

En el repositorio podemos consultar la implementación de un componente (o container en el vocabulario React) que carga más información a partir de un usuario de Github. En este caso estamos cargando cuatro recursos distintos con await. Este uso de await hace que para empezar a cargar el segundo recurso, sea necesario que el primero ya haya sido devuelto por el servidor de Github, por lo que no estamos aprovechando una posible concurrencia que nos permitiría tener la información antes y cargar los cuatro recursos en paralelo.

Aunque el tiempo de carga está bien, y puede que por simplicidad prefiramos tener el código con async/await, debemos conocer mecanismos para poder cargar N recursos de manera concurrente, ya que en muchas aplicaciones no podemos dictar el diseño de las APIs desde las que tomamos la información y puede que nos sea útil cargar toda esta información para mostrar listados a los usuarios de nuestra aplicación.

Una solución para problemas como este es usar Promise.all. Esta función estándar de Javascript permite esperar a que N promesas se resuelvan o cualquiera de ellas falle. En este container vemos como cargamos los cuatro recursos con Promise.all. Abajo está también el código en el que vemos como se maneja como si la promesa devuelta de Promise.all fuera la unión de las N promesas.

Usando las herramientas de desarrollo de Chrome podemos ver como si cargamos en la vista el componente UserDashboardAppPromiseAll o UserDashboardAppAsyncAwait vemos la diferencia entre cargar los cuatro recursos de manera concurrente y la línea temporal en la que un recurso se carga tras otro.

Conclusiones

Aunque no todos los navegadores soportan async/await, con Babel podemos utilizar esta funcionalidad sin tener que preocuparnos de si nuestro código es soportado directamente o no.

Algunos frameworks de desarrollo con Javascript, como React Native incorporan async/await de casa, por lo que en muchos casos nos será más natural utilizarlo que usar los métodos de la clase Promise. Espero que con este pequeño tutorial os sea más fácil usarlo en vuestro código y conocer su potencia.