Para definir Node.JS mejor viene bien observar algunas características de la plataforma y las diferencias de NodeJS con Javascript común y con otros lenguajes de programación.
Este Manual de Node.JS está creado a través de unas charlas #nodeIO que hemos realizado en DesarrolloWeb.com, de las cuales estos artículos son una transcripción. En este punto estamos todavía en la primera charla, en la que Alejandro Morales nos está aclarando algunos conceptos importantes para poder entender bien qué es NodeJS.
En el artículo anterior vimos cómo se instalaba esta NodeJS y en el presente texto veremos en detalle algunas de las características más fundamentales de NodeJS. Entre todas ellas nuestro ponente Alejandro Morales destacó: JavaScript sin limitaciones, Programación asíncrona y Programación orientada a eventos. Todos estos puntos los veremos a continuación.
Un Javascript "sin restricciones"
Con NodeJS tenemos un "Javascript sin restricciones", tal como afirma @_alejandromg, ya que todo se ejecuta en el servidor y no tenemos que preocuparnos de si nuestro código será compatible o no con distintos clientes. Todo lo que escribas en Node JS y te funcione en tu servidor, estarás seguro que funcionará bien, sea cual sea el sistema que se conecte, porque toda la ejecución de código del servidor se queda aislada en el servidor.
Pero no sólo eso, el Javascript original tiene algunas estructuras de control que realmente no se utilizan en el día a día, pero que realmente existen y están disponibles en NodeJS. En algunas ocasiones resulta especialmente útil alguna de las mejoras de Javascript en temas como la herencia.
Otro ejemplo es que en Javascript haces:
for(var key in obj){ }
Mientras que en las nuevas versiones de Javascript podrías hacer esto otro:
Object.keys(obj).forEach()
Programación Asíncrona
Éste es un concepto que algunas personas no consiguen entender a la primera y que ahora toma especial importancia, dado que NodeJS fue pensado desde el primer momento para potenciar los beneficios de la programación asíncrona.
Imaginemos que un programa tiene un fragmento de código que tarda cinco segundos en resolverse. En la mayoría de los lenguajes de programación precedentes, durante todo ese tiempo el hilo de ejecución se encuentra ocupado, esperando a que pasen esos cinco segundos, o los que sea, antes de continuar con las siguientes instrucciones. En la programación asíncrona eres capaz de liberar el proceso de modo que los recursos se quedan disponibles para hacer otras cosas durante el tiempo de espera.
Un ejemplo claro de esto es una llamada a un servicio web, o una consulta a la base de datos. Una vez realizada la solicitud generalmente pasará un tiempo hasta que se obtenga la respuesta. Ese tiempo, por corto que sea, dejaría un proceso esperando en la programación tradicional y en la asíncrona simplemente se libera. En NodeJS, o en Javascript en general, cuando esa espera ha terminado y se ha recibido la respuesta, se retomará la ejecución del código. Para definir las acciones a realizar (código a ejecutar) cuando se haya terminado esa espera, se especifica el código mediante funciones llamadas habitualmente "callbacks". Esas funciones contendrán las líneas de código que ejecutar al final de esos procesos de espera, y una vez se ha recibido la respuesta.
La filosofía detrás de Node.JS es hacer programas que no bloqueen la línea de ejecución de código con respecto a entradas y salidas, de modo que los ciclos de procesamiento se queden disponibles durante la espera. Por eso todas las APIs de NodeJS usan callbacks de manera intensiva para definir las acciones a ejecutar después de cada operación I/O, que se procesan cuando las entradas o salidas se han completado.
Por ejemplo miremos este código:
console.log("hola");
fs.readFile("x.txt", function(error, archivo){
console.log("archivo");
})
console.log("ya!");
Realmente Javascript es primeramente síncrono y ejecuta las líneas de código una detrás de otra. Por ese motivo, como resultado de ejecución del código anterior, primero veremos el mensaje "hola" en la consola, luego el mensaje "ya!" y por último, cuando el fichero terminó su lectura, veremos el mensaje "archivo".
Por la forma de ejecutarse el código se puede entender la programación asíncrona. La segunda instrucción (que hace la lectura del archivo) tarda un rato en ejecutarse y en ella indicamos además una función con un console.log ("archivo"), esa es la función callback que se ejecutará solamente cuando termine la lectura del archivo. Por ese detalle, lo último que aparecerá en pantalla es el contenido del fichero. Este detalle es de extrema importancia para entender la programación con NodeJS.
Problema del código piramidal
El uso intensivo de callbacks en la programación asíncrona produce el poco deseable efecto de código piramidal, también conocido habitualmente como "código Espagueti" o "callback hell". Al utilizarse los callbacks, se meten unas funciones dentro de otras y se va entrando en niveles de profundidad que hacen un código menos sencillo de entender visualmente y, por tanto de mantener durante la vida de las aplicaciones.
La solución es hacer un esfuerzo adicional por estructurar nuestro código. Básicamente se trata de modularizar el código, escribiendo cada función aparte e indicando solamente su nombre cuando se define el callback. Podrías incluso definir las funciones en archivos aparte y requiriéndolas con require("./ruta/al/archivo") en el código de tu aplicación. Todo esto lo veremos con detalle en numerosos ejemplos en el Manual de NodeJS, por lo que no te debes de preocupar mucho si todavía no lo entiendes.
Al conseguir niveles de indentación menos profundos estamos ordenando el código, con lo que será más sencillo de entender y también más fácil de encontrar posibles errores. Además, a la larga conseguirás que sea más escalable y puedas extenderlo en el futuro o mantenerlo por cualquier cuestión.
Algunos consejos a la hora de escribir código para que éste sea de mayor calidad:
- Escribe código modularizado (un archivo con más de 500 líneas de código puede que esté mal planteado)
- No abuses, no repitas las mismas cosas, mejor reusa.
- Usa librerías que ayuden al control cuando lo veas necesario (como async que te ayuda a ordenar ese montón de callbacks)
- Usa promesas y futuros
- Conoce el lenguaje y mantente al día con las novedades de las presentes y futuras versiones de Javascript ES6, ES7...
Programación orientada a eventos (POE)
Conocemos la programación orientada a eventos porque la hemos utilizado en Javascript para escribir aplicaciones del lado del cliente. Estamos acostumbrados al sistema, que en NodeJS es algo distinto, aunque sigue el mismo concepto.
En Javascript del lado del cliente tenemos objetos como "window" o "document" pero en Node.JS no existen, pues estamos en el lado del servidor.
Eventos que podremos captar en el servidor serán diferentes, como "uncaughtError", que se produce cuando se encuentra un error por el cual un proceso ya no pueda continuar. El evento "data" es cuando vienen datos por un stream. El evento "request" sobre un servidor también se puede detectar y ejecutar cosas cuando se produzca ese evento.
Volvemos a insistir, NodeJS sería un equivalente a PHP, JSP, ASP.NET y entonces todo lo que sean eventos de Node, serán cosas que ocurran en el lado del servidor, en diferencia con los eventos de Javascript común que son del lado del cliente y ocurren en el navegador.
Conclusión
Poco a poco vamos entendiendo qué es (y que NO es) NodeJS. Son todo conceptos que resultarán nuevos para muchos de los lectores y por ello era importante dejarlos claros. Ahora vamos a pasar a explicar algunas otras cosas de interés que utilizamos en el día a día en esta plataforma, como el gestor de paquetes npm.
Miguel Angel Alvarez
Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...