node (3): event loop

Una primera aproximación al event loop de node

Aleksandra Ekster

archivado en: JavaScript / 2 mayo, 2015 / taller:

Antes de seguir viendo código de node, vamos con un par de ideas teóricas que luego nos permitirán entender mejor las cosas prácticas. Empezamos con el denominado event loop.

Muchos lenguajes de servidor, como php contra apache, ejecutan las tareas de forma «bloqueante», es decir, se esperan a terminar una operación para lanzar la siguiente. Por ejemplo, en pseudo-php, una petición a la página de una biblioteca virtual podría seguir estos pasos de forma ordenada:

1. Decodificar la petición request.

2. Lanzar una query contra la tabla «Páginas» donde el id de la página es 3.

3. Compilar una plantilla html.

4. Devolver la plantilla compilada para que la interprete el navegador.

Y hasta que no termina cada uno, hasta que no obtiene los datos de la query, por ejemplo, no pasa al siguiente, lo que se puede convertir en segundos e incluso minutos en los que la «máquina» está simplemente a la espera. En esos casos los servidores son capaces de lanzar muchas tareas en paralelo, por lo que pueden resolver los procesos que demanda cada petición sin colapsarse. Sin embargo, cuando les llegan muchas peticiones, cuando hay miles y miles de personas pidiendo un recurso alojado en un server, todo se ralentiza e incluso se atasca.

Node, por el contrario, trabaja de forma concurrente. Va haciendo las cosas de a poquitos a medida que se necesitan. Así, por ejemplo, mientras espera a que la query sea devuelta se pone a hacer otras cosas, lo que optimiza el trabajo de la «máquina.

Jonathan MirCha explica esto con un ejemplo que para los legos en la materia, como es mi caso, resulta muy ilustrativo. Un servidor tradicional, un apache con php, sería como un restaurante con muchas mesas cada una de las cuales está atendida por un camarero. Mientras los comensales van dando cuenta de los platos, cada camarero está quieto al lado de la mesa que le corresponde sin hacer nada esperando que les pidan el siguiente. En node, en cambio, hay muy pocos camareros, pero están en constante movimiento y solo le dedican a cada cliente el tiempo preciso para ir a la cocina y llevarles el pedido y solo atienden una mesa cuando son requeridos, es decir, cuando se produce un evento. Es lo que se conoce como el patrón reactor, sobre el que profundizaremos en las siguientes entradas. Y una de las maneras en que node consigue esto, el que los camareros no dejen de trabajar, es mediante las funciones callback. Veamos esto con más detalle.

Durante el desarrollo resulta muy cómodo trabajar con nodemon, un observador que relanza la aplicación cuando detecta un cambio en el código. Se instala con el npm:

npm install -g nodemon

Y luego en vez de lanzar la aplicación con node ..., lo hacemos con nodemon.

nodemon miAplicacion.js

Funciones callback y métodos asíncronos

Small is beautiful, «lo pequeño es hermoso», este es otro de los puntos de fuga de node, que cuenta con un core sencillo formado por unos pocos módulos relativamente simples. Ya vimos uno de ellos, el módulo http, que sirve para levantar un server, y cómo podemos incorporarlo a un archivo mediante el método require.

/*  mi-server.js */

var http = require('http');

http.createServer(function(req, res) {

res.writeHead(200, {'Content-Type': 'text/plain'});

res.end('Hola Mundo')

}).listen(3000);

console.log("Servidor funcionando en localhost:3000. Ctrl+C para terminar");

Aunque aún nos queda mucho por conocer del módulo http, vamos a ir viendo otro: el fs, que sirve para trabajar con archivos estáticos (files system). El funcionamiento básico es muy sencillo, se carga el módulo y luego podemos acceder a sus métodos. El que nos interesa ahora es readFile(), que como indica su nombre lee un archivo. Además de la ruta del archivo a cargar, este método puede recibir otros dos parámetros: la codificación y una función callback.

"use strict"

var http = require('http'),

fs = require('fs');

http.createServer(function(request, response) {

response.writeHead(200, {"Content-Type":"text/html"});

fs.readFile("index.html", "utf-8", function(error, data) {

if ( error ) {

response.end("Error abriendo el archivo");

} else {

response.end(data);

}

});

}).listen(3000);

Una función callback en js es una función que se pasa como argumento a otra, donde la podemos ejecutar cuando lo necesitemos. En un ejemplo muy sencillo:

function miFuncionUsaCallback(miFuncionCallback) {

console.log("bar");

miFuncionCallback();

}

miFuncionUsaCallback( function(){

console.log"foo")

});

// bar, foo

Por ver otro ejemplo, son las funciones que se pasan con los eventos habituales de jQuery:

$('miSelector).click(function() {

// patatín y patatán...

});

Y lo interesante en lo que nos atañe es que estas funciones «de vuelta» se pueden invocar cuando termina un proceso, lo que nos permite controlar el flujo de la aplicación de manera no bloqueante, como en el caso anterior en el que hemos leído un archivo y lo hemos enviado, de manera asíncrona -una vez terminado el proceso de lectura- con una función callback. Esto se entiende bien si incluimos unos cuantos console.logs en el código de antes:

...

console.log(0);

response.writeHead(200, {"Content-Type":"text/html"});

console.log(1);

fs.readFile("index.html", "utf-8", function(error, data) {

console.log(2);

if ( error ) {

response.end("Error abriendo el archivo");

} else {

console.log(3);

response.end(data);

}

});

console.log(4);

...

Si ejecutamos el código, la secuencia en la que aparecerían los logs sería: 0, 1, 4, 2, 3, ya que esa función se ejecuta una vez que se ha leído el archivo index.html. En cambio, si refactorizáramos el código de esta manera, eliminando la función callback, estaríamos provocando una operación bloqueante, que es lo peor que puede suceder en una aplicación node.

...

http.createServer(function(request, response) {

console.log(0);

response.writeHead(200, {"Content-Type":"text/html"});

console.log(1);

var pagina = fs.readFile("index.html");

console.log(2);

response.end(pagina);

console.log(3);

// Donde la secuencia sería 1, 2, 3

...

Bueno, pues con la esperanza de que este tema tan complicado haya quedado un poco más claro, por hoy lo dejo aquí.

|| Tags: , ,

valoración de los lectores sobre node (3): event loop

  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • 5 sobre 5 (1 votos)

¿Te ha parecido útil o interesante esta entrada?
dormido, valoración 1 nadapensativo, valoración 2 un poco sonrisa, valoración 3 a medias guiño, valoración 4 bastante aplauso, valoración 5 mucho

Tú opinión es muy importante, gracias por compartirla!

Los comentarios están cerrados.