Node.js Async

why

Ultimamente he estado desarrollando backend en Node.js y he encontrado algunos problemas, sobretodo al hacer llamadas a la base de datos dentro de un loop. De hecho he tenido que utilizarlo al montar un servicio “what’s hot” con node.js y redis.

Node.js se ejecuta en un solo ciclo, donde hay eventos en bucle, y aprovecha las llamadas asincrónicas para hacer varias cosas, como las operaciones de E/S. Mientras que otros lenguajes envían una consulta a la base de datos y esperan hasta recibir el resultado, Node.js no. Cuando se envía una consulta de base de datos, Node.js continuará ejecutando el código que viene después, y luego salta hacia atrás cuando el resultado está disponible.

Este es un concepto poderoso que permite mejoras en la eficiencia, pero que ocasionalmente requiere un poco más de trabajo por nuestra parte para hacer frente a determinadas situaciones. Una de esas situaciones con las que me he peleado ultimamente con bastante frecuencia es la necesidad de esperar a que un número de operaciones asincrónicas terminen antes de ejecutar un código adicional.

Por ejemplo, si tienes una serie de elementos que desea guardar en la base de datos, y además los estás insertando dentro de un bucle for. Una vez que todos están almacenados, deseamos ejecutar una función que calcula algunas estadísticas.

En principio, nosotros haríamos lo siguiente:

Como se puede ver en los comentarios anteriores, tenemos un problema. Si observamos lo que ocurre, vemos que el doSomethingOnceAllAreDone() se ejecuta antes que las llamadas asíncronas.

Os muestro otro código para reforzar el concepto:

Y veremos que la consola mostrará:

Es decir, primero devuelve “all dropped” (que es la última instrucción)  y después los “dropped” que nosotros esperábamos primero.

Una de las mejores soluciones a este problema es utilizar el paquete node-async que incluye una serie de funciones para hacer frente a situaciones como ésta. Vamos a resolver este problema utilizando dos características node-async diferentes. Por un lado async.each() y por otro lado async.parallel().

Para poder utilizarlo, debemos incluir en nuestro fichero Package.json las depencias correspondientes:

A continuación lo instalaremos:

 

async.each(arr, iterator, callback)

En primer lugar, vamos a ver la función async.each(). Esta es la solución más sencilla para el problema.

  • arr – El array que contiene los elementos. Sobre él iteraremos.
  • iterador (item, callback) – Una función para aplicar a cada elemento de la matriz. Esta función iterator se pasa a callback(err) que debe ser llamado una vez que se ha completado.
  • callback (err) – Un callback que se llama después de que todas las funciones de iterator han terminado o si ha producido un error.

Importante. Dado que esta función aplica el iterator para cada elemento en paralelo, no hay garantía de que las funciones de iterator se completen en orden. Vamos a verlo más claro en código:

async.parallel()

La solución anterior funciona bien si sólo hay que iterar sobre una colección, pero ¿qué ocurre si tenemos una situación más compleja? En lugar de iterar sobre una colección, async.parallel() nos permite hacer push de un montón de (potencialmente relacionadas) llamadas asincrónicas de un array. Básicamente lo que hace es crear un array de tareas, que serán ejecutadas y a continuación llamará a una función cuando hayamos terminado. Realmente esta es la solución a mi problema inicial, que era poder acceder a una base de datos varias veces dentro de un bucle.

Y esto ha sido todo! Ya sabemos cómo programar llamadas asíncronas en node.js

2 Responses to “Node.js Async”

  1. […] a poner el código que resuelve este problema. Si queréis ver más información, podéis acceder a nuestro post sobre llamadas asíncronas en Node.js. Antes de ejecutarlo debéis hacer dos cosas. La primera, configurar las dependencias de async es […]

  2. Eduardo Beltran C. dice:

    Para las llamadas Asyncronas dentro de JS también se pueden usar “callbacks”.

Deja un comentario