Utilice el bucle de eventos de Node.js de forma efectiva – IBM Developer

Utilice el bucle de eventos de Node.js de forma efectiva

Para los nuevos desarrolladores de aplicaciones de Node.js, parte de la curva de aprendizaje es estar cómodo sobre cómo funciona el bucle de eventos de única hebra y cómo este puede provocar resultados inesperados. Puede practicar el uso del bucle de eventos en las 3 muestras interactivas de este tutorial. En poco tiempo usted estará escribiendo código rápido y eficiente que maneja llamadas asíncronas fácilmente.

Echemos un vistazo a tres sencillos fragmentos de código para ilustrar cómo funciona el bucle de eventos.

Muestra 1: un ejemplo sencillo

Try icon La primera muestra define tres funciones y las llama. Haga clic en Ejecutar para ejecutar el código. Después intente cambiar los valores numéricos de las llamadas en el setTimeout() para ver cómo cambia el resultado. Por ejemplo, haga que todos los valores sean 0.

Es posible que haya esperado que la muestra original genere este resultado:


mundo!
¡Hola
a todos,

Sin embargo, este no es el caso. printNow('Hello') se ejecuta primero, después printSoon('there,'), y después printEventually('world!'). Eso pasa porque cuando el motor de Node ve setTimeout() en la llamada printEventually() , se la envía al SO y después pasa a la llamada de printNow(). Ese código es asíncrono, así que el mensaje Hello se imprime inmediatamente. Finalmente, la llamada a setTimeout() de printSoon() se pasa al SO. Un segundo más tarde (printSoon() espera 1000 milisegundos), el cronómetro de setTimeout() de printSoon() expira, así que el mensaje a todos, se imprime en la consola. Un segundo después de eso, el último setTimeout() expira y se muestra world! shows up. Esa es la razón por la que las tres declaraciones se procesan en este orden:


¡Hola
a todos, mundo!

Cómo funciona el bucle de eventos

Los servidores web tradicionales tienen múltiples hebras, y cada sesión normalmente tiene su propia hebra. Ese enfoque funciona, pero necesita que el servidor web asigne recursos que no se utilizan cuando la sesión está inactiva. La sobrecarga de esas sesiones ociosas hace que sea más difícil escalar el servidor para manejar los picos de la demanda.

Por otro lado, el motor de Node tiene una única hebra que maneja todas las notificaciones del sistema operativo que indican que algo está listo para actuar sobre él. Si esa cosa es asíncrona (por ejemplo, una llamada a una base de datos o a una interfaz de REST), el motor de Node pide al sistema operativo que le notifique cuando una llamada esté lista para ser procesada (por ejemplo, cuando han llegado datos de la base de datos o de la llamada de REST). Mientras tanto, el bucle de eventos de Node pasa a la siguiente tarea que se tiene que hacer.

Sepa que el motor de Node se encarga de todo inmediatamente. Solo que, en algunos casos, «inmediatamente» significa pedir al sistema operativo que le avise cuando algo esté listo para ser procesado. Para más información acerca de cómo ocurre la magia, vea los videos que están enumerados de la sección «Temas relacionados» que está al final de este tutorial.

Muestra 2: El patrón de la devolución de llamada

Aunque la primera muestra ilustra la forma en que Node maneja el código asíncrono, normalmente se llama al código asíncrono con el patrón de la devolución de llamada. El patrón se parece a esto:

Listado 1. Pseudocódigo para definir una función asíncrona
            
function asyncCode(arg1, arg2, callback) {

  // Haz lo que la función hace aquí. Cuando haya acabado, debería invocar
  // la función de devolución de llamada, regresando un objeto de Error de Javascript (que esperemos que sea nulo)
  // y los resultados de esta función.

  return callback(error, results);
}

El último parámetro que se pasa a asyncCode() es otra función. Cuando asyncCode() finaliza su trabajo, invoca a la función de devolución de llamada que se le pasó. Por convención, la función asíncrona pasa un objeto de Error de Javascript como primer parámetro para la devolución de la llamada, seguido por cualesquiera resultados que haya generado la función asíncrona. Observe que la función asyncCode() puede tener todos los argumentos que necesite, y puede pasar a la función de devolución de llamada todos los argumentos que sean necesarios.

Así es como se define una función asíncrona. Así es como se llama a uno:

Listado 2. Pseudocódigo para llamar a una función asíncrona
            
asyncCode(x, 37, function(error, results) {
  if (error != null)
    // Manejar el error si hay uno más
    // En caso contrario, hace lo que queremos con los resultados
});

Try icon Esta versión de código utiliza funciones de devolución de llamada. Haga clic en Ejecutar para ejecutar el código tal como está. Después intente cambiar los valores numéricos de las llamadas de printMessage() para ver cómo cambia el resultado. Pruebe reemplazar console.log('¡Hola') con otra llamada a printMessage(). ¿Qué ocurre si el valor de un timeout es cero? ¿Eso afecta al pedido de la ejecución?

La función printMessage() implementa el patrón de devolución de llamada. Establece un tiempo límite, lo que provoca que Node pase el tiempo límite al SO. Después, Node pasa a lo siguiente. En este caso, lo siguiente es una sencilla llamada a console.log(). Después de eso es otra llamada a printMessage(), que establece otro tiempo límite. El código finaliza a medida que expiran los tiempos límite y a todos, y mundo! se escriben en la consola. Las funciones de devolución de llamada generan el mismo mensaje que la primera muestra:


¡Hola
a todos, mundo!

Muestra 3: anidar devoluciones de llamadas

Si, por una razón, quisiera que las tres palabras del mensaje se imprimiesen en un orden en particular, tendría que anidar las funciones de producción de devolución de llamada. Por ejemplo, si los parámetros de timeout fuesen números generados aleatoriamente entre 0 y 5000, no tendría forma de saber cuál sería el mensaje final.

Try icon Haga clic en Ejecutar para ejecutar el código tal como está. Ahora, intente cambiar los valores numéricos de las llamadas de printMessage() . No importa los valores que utilice, el código los ejecuta en el mismo orden.

Este código garantiza que estas tres invocaciones de printMessage() ocurran en un orden específico. La primera llamada a printMessage() pasa en una función de devolución de llamada que también llama a printMessage(), la cual, a cambio, pasa otra función de devolución de llamada que llama a printMessage(). El código genera este saludo incomprensible:


mundo!
¡Hola
a todos,

Es relativamente fácil leer el código porque ignoramos el manejo de errores y sólo tenemos una línea de código antes de volver a llamar a printMessage(). Si incluimos el manejo de errores al código y tenemos una lógica compleja entre las llamadas, esto rápidamente puede provocar un infierno de devoluciones de llamadas, en el que el código está anidado en varios niveles y es difícil de entender. Vea la sección «Temas relacionados» para obtener más información sobre el hell de devolución de llamadas.

Conclusión

Este es un vistazo rápido a cómo utilizar el bucle de eventos de hebra única de Node.js. A medida que utiliza bibliotecas de Node para acceder a bases de datos, archivos y cosas de ese tipo, el saber cómo manejar métodos asíncronos — y cómo asegurarse de que el código se ejecuta en un determinado orden — son habilidades cruciales. Si entiende bien el bucle de eventos, podrá escribir un código rápido y eficiente que maneja llamadas asíncronas fácilmente.

Acerca de nuestras muestras interactivas

Las muestras de código interactivas de este tutorial utilizan el espacio limitado de developerWorks. El espacio limitado se implementa en un entorno de OpenWhisk que se ejecuta en IBM Bluemix®. Busque mucho más contenido interactivo que llegará en las próximas semanas y meses. (y, como siempre, lo invitamos a que se registre para obtener una cuenta de Bluemix gratuita y vea lo que la plataforma IBM Cloud puede hacer.)

Agradecimientos

Al autor le gustaría agradecer a Sam Roberts, de IBM, por su ayuda para corregir los errores y malentendidos de la versión original de este tutorial. Cualquier otra falsedad es un error exclusivo del autor.