jueves, 20 de agosto de 2015

Trasteando con Node.js y JavaScript

De un tiempo a esta parte me estoy aficionando a los MOOC (Massive Open Online Course). Me parece una magnífica manera de satisfacer mi curiosidad y de enriquecerme un poquito profesionalmente, ¡Y por un precio muy módico! De hecho creo que de disponer de más tiempo podría llegar a convertirse en una verdadera adicción afición. Hace algún tiempo conocí una plataforma en castellano (también en portugués) que se llama MiriadaX. Hice con ellos un curso de HTML5, CSS y Javascript, desarrollado por la Universidad Politécnica de Madrid y, salvando los inconvenientes y pequeñas carencias de este tipo de plataformas, me gustó bastante y me dejó cierto poso, sobre todo en lo referente a hojas de estilo. 

Hace poco he terminado otro, también de la Politécnica de Madrid, sobre servicios en la nube y Node.js. No es que me vaya a convertir en un experto en Node.js (Probablemente eso no ocurrirá en un plazo de tiempo razonable... ¡más quisiera!) pero me ha dado dado una buena overview y sobre todo me ha dejado con ganas de aprender más. Como estas cosas, si no se practican, empiezan a olvidarse casi en el mismo momento en el que presentas el último ejercicio, me hubiera gustado revisar profundamente todos los temas del curso en el blog, pero lo cierto es que el tiempo del que dispongo es muy limitado, así que no voy a hacerlo; aunque probablemente sí que iré dejando algunas píldoras... Dicen que tratando de explicar algo es como mejor termina de comprenderlo uno mismo. Sea, al menos, por tener donde agarrarme cuando quiera recordar alguna de estas cosas... 


Hasta ahora para mi JavaScript era esa cosa simpática que me permitía introducir comportamientos chulos en las páginas web cuando se ejecutaban en el lado del browser. Luego conocí (sólo un poco) jQuery y eso ya era una pasada... Con Node.js y su JavaScript en el lado del servidor te empiezas a dar cuenta de que JavaScript es un lenguaje de programación completo. Es cierto, como hablaba con un amigo -también autor de este blog-, que adentrarse en la programación seria en JavaScript da una pereza enorme, en algunos aspectos cambia mucho la forma de hacer las cosas. Sin embargo JavaScript se me antoja muy potente y creo que merece la pena echarle un vistazo. 

Node.js es un interprete de JavaScript que trabaja en el lado del servidor y que está concebido para construir aplicaciones muy escalables y manejar miles de conexiones simultaneas en una única máquina física. Hoy día existen muchos frameworks y módulos (instalados o fácilmente instalables en Node.js) que ayudan bastante a perder el miedo que produce tener que hacer todo "desde el principio". Por ejemplo, una vez que has instalado Node.js en tu equipo, poner en marcha un servidor HTTP es tan sencillo como esto:

var http = require("http");

http.createServer(function(request, response) {
  response.writeHead(200, {"Content-Type": "text/html"});
  response.write("Hola Mundo");
  response.end();
}).listen(9000);

Metiendo este código en un archivo .js (por ejemplo server.js) y ejecutando "node server.js" en la consola, tienes corriendo un servidor HTTP que despliega una página web con un precioso y tradicional "Hola Mundo". Basta comprobarlo en el puerto 9000 de tu localhost.

Pero veamos que pasa aquí... En la primera línea cargamos mediante require el módulo "http" (que viene por defecto en la instalación de Node), el cual hacemos accesible a través de la variable del mismo nombre. Luego la función createServer del módulo "http" devuelve un objeto que a su vez tiene un método llamado listen que toma un valor numérico que indica el puerto en que nuestro servidor HTTP tiene que escuchar... Vale ¿Pero que rayos hace esa función dentro del paréntesis?... 

Esta es una de las cosas chulas (y raras) de JavaScript: la definición de una función justo donde esperarías ver el primer parámetro de la llamada a createServer(). Precisamente la declaración de esa función es el primer (y único) parámetro que le vamos a pasar a createServer(). En JavaScript las funciones se pueden pasar en la declaración de parámetros como otro valor primitivo u objeto... Por lo que a la inicialización del servidor se refiere nos hubiera bastado con esto:

var http = require("http");
var server = http.createServer();
server.listen(9000);

Con lo cual hubiéramos puesto en marcha el servidor en el puerto 9000 sin más, pero si vamos a montar un servidor ¿Por qué no darle, así ya de entrada, algo que servir?

Cuando se define una función como parámetro de otra, la función que actúa como parámetro no necesita un nombre, la llamamos función anónima. También podríamos haber definido una function onRequestSayHello(request, response) fuera de la zona de declaración de parámetros, haberla asignado a una variable y luego haber referenciado dicha variable a la hora de pasar los parámetros al método createServer(), más o menos así:

var http = require("http");

function onRequestSayHello(request, response) {
  response.writeHead(200, {"Content-Type": "text/html"});
  response.write("Hola Mundo");
  response.end();
}

http.createServer(onRequestSayHello).listen(9000);

Igual así es más legible (pero menos pro)... Sin embargo, lo interesante aquí -y lo que a priori a mi me costaba más visualizar- es que no estamos pasando como parámetro el valor de retorno de una función, sino la función misma... Vale, acepto que en JavaScript puedo pasar una función como parámetro de otra función ¿Pero por qué funciona esto? 

Node.js sigue el paradigma de la programación orientada a eventos. Esto quiere decir, en el caso de nuestro servidor por ejemplo, que una petición HTTP puede llega en cualquier momento, esto es, de manera asíncrona. En otros lenguajes de programación (como PHP) cada vez que llega una petición HTTP, el servidor (pongamos un Apache) genera un nuevo hilo de proceso para atender esa petición. Pero en Node sólo tenemos un hilo sobre el que corre nuestro servidor; así que ¿Cómo nos las apañamos -sin volvernos locos con el control de flujo- para gestionar una petición en el puerto 9000, que llega así, sin avisar, mientras estamos ejecutando tranquilamente nuestro programa en Node.js?

Cuando creamos el servidor y le pasamos una función al método que lo crea, cada vez que nuestro servidor recibe una petición, la función pasada se invoca y ejecuta su código. El servidor está cíclica y continuamente comprobando si se producen eventos de algún tipo, en nuestro caso una petición HTTP (es lo que se llama bucle -o loop- de eventos). Si los eventos no llegan, el hilo continúa su ejecución, pero si llega un evento -la petición HTTP- el sistema sabe que hay una función para atenderlo (porque se lo dijimos al crear el servidor) y ejecuta su código. Esta vuelta atrás para llamar a la función que gestiona el evento es lo que se llama callback.

Podemos ver que esto funciona modificando ligeramente el código de nuestro servidor: 

var http = require("http");

http.createServer(function(request, response) {
  console.log("Petición Recibida.");
  response.writeHead(200, {"Content-Type": "text/html"});
  response.write("Hola Mundo");
  response.end();
}).listen(9000); 
 
console.log("Servidor Iniciado.");

Si ejecuto ahora mi servidor con "node server.js", inmediatamente (antes de abrir siquiera el navegador) veré en consola el mensaje "Servidor Iniciado". En cuanto abramos el navegador y escribamos "http://localhost:9000/" veremos nuestro "Hola Mundo" en el browser y el mensaje "Petición Recibida" en la consola... JavaScript en el lado del server, asíncrono, orientado a eventos y callback en acción...

Por ahora es suficiente, voy a digerir esto...

No hay comentarios:

Publicar un comentario