Skip to content

Instantly share code, notes, and snippets.

@gabmontes
Last active September 29, 2015 17:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gabmontes/b491f3fc8d3e1327cb86 to your computer and use it in GitHub Desktop.
Save gabmontes/b491f3fc8d3e1327cb86 to your computer and use it in GitHub Desktop.

Introducción a Node.js

Objetivos

  • Conocer la motivación que llevó a la creación de Node.js y su arquitectura interna básica para tener suficiente información y poder tomar decisiones informadas acerca de si Node.js es la herramienta adecuada para resolver un problema específico.
  • Conocer y comprender objetos y clases base que provee la plataforma. Entender el sistema de módulos que usa Node.js y algunos módulos base muy importantes.

Introducción y arquitectura

Node.js es un ambiente de ejecución de aplicaciones JavaScript basado en el motor V8, uno de los componentes principales del navegador Google Chrome. Utiliza un modelo de programación basado en eventos, con I/O no bloqueante y se complementa con NPM y módulos que siguen la arquitectura CommonJS para extender su funcionalidad. Fue creado en 2009 por Ryan Dahl en Joyent y actualmente administrado por la Linux Foundation.

La motivación de su desarrollo fue lograr que los servidores de aplicaciones manerjaran los eventos de I/O de manera asíncrona con un sistema basado en eventos, a diferencia de uno de los servidores más populares de la época, Apache, que está basado en un esquema multithreaded y degrada su performance e incrementa linealmente el uso de recursos del servidor a medida que aumenta la carga de usuarios.

Node.js está escrito en C++ e integra V8, motor de ejecución de JavaScript, con libuv, librería que maneja event-loops y permite realizar operaciones de I/O asíncronas, como por ejemplo: acceso al sistema de archivos, a protocolos TCP y DNS.

Node.js fue desarrollado bajo la supervisión de Joyent hasta la versión 0.12. Problemas internos dentro del equipo técnico hicieron que a fines de 2014 se dividiera dicho equipo y ahí comenzó el trabajo sobre io.js, versión basada en Node.js pero con nuevas características como ser versiones de V8 actualizadas y soporte para ES6. A mediados de 2015, luego de que io.js hubiera recibido mucho apoyo por parte de la comunidad, se decidió volver a unir los proyectos, lo que derivó en el traspaso de la gestión a la Linux Foundation y la versión 4.0.0 de Node.js, liberada en septiembre de 2015.

REPL

Node.js trae integrado un ambiente de ejecución o REPL (Read-Eval-Print-Loop) que permite ingresar comandos y ejecutarlos inmediatamente. Para acceder, solo se debe ejecutar el comando:

> node

En la línea de comandos se tiene acceso completo a todas las funcionalidades de Node.js. Este entorno estuvo pensado desde el principio para tener la mayor similitud al entorno de ejecución de JavaScript en un navegador. Por ejemplo, para demorar la ejecución de una función en una página web se utiliza la función setTimeout que se encuentra disponible en el objeto global. En Node.js, también existe una función setTimeout con la misma sintaxis y propósito, y también es parte del objeto global del entrono de ejecución. Así, muchas otras propiedades, objetos y funciones fueron replicadas desde el entorno de los navegadores al entorno de Node.js.

Objeto process

Por ejemplo, un objeto que es exclusivo de Node.js es process, a través del cual se tiene acceso a funciones de entrada/salida y manejo del propio proceso de ejecución. Por ejemplo, para forzar a que termine un programa de Node.js se debe ejecutar:

process.exit(); // fuerza a que el procesamiento termine

Para recibir una notificación de que el programa va a ser terminado por el sistema operativo, y poder tomar acciones al respecto, se debe escuchar el evento SIGINT:

process.addListener("SIGINT", function () { // escucha `Ctrl-C` o `kill -2`
    console.log("Terminando el proceso...");
    process.exit(1);
});
setInterval(function () {
    console.log(process.pid); // imprime el id de proceso actual
}, 1000);

El objeto process también tiene otras propiedades muy frecuentemente usadas como env, que contiene todas las variables de entorno del sistema operativo, stdin/stdout/stderr, para manejar la entrada/salida del process, argv, conteniendo los parámetros con los que fue invocado el proceso, etc.

Ejercicios

Ejercicio 1

Escribir un programa que reciba números por línea de comandos y los sume.

> node suma.js 2 5 12
> 19

Ejercicio 2

Extender el programa anterior para que sume o saque promedio de acuerdo a una variable ambiente NODE_OP (suma o prom).

> NODE_OP=prom node op.js 2 5 12
> 6.333333333333333

Módulos y CommonJS

Node.js utiliza un sistema de módulos que permiten extender la funcionalidad básica del entorno, basado en el proyecto CommonJS. El objetivo de este proyecto, iniciado en 2009 por Kevin Dangoor de Mozilla, era generar un ecosistema de JavaScript que se extendiera más allá de los navegadores e incluyera diferentes entornos de programación como servidores y programas de escritorio.

Hay una serie de módulos que forman parte del entorno de Node.js y que pueden ser utilizados directamente. Por ejemplo, para realizar operaciones con el sistema de archivos, se debe hacer lo siguiente:

var fs = require("fs");
fs.readFile("ruta/al/archivo", callback);

Los módulos son, entonces, instanciados utilizando la función global require(), la que devuelve un objeto conteniendo todas las propiedades públicas, objetos y funciones, que forman parte de la interfaz (API) de dicho módulo.

Módulo fs

Éste módulo nos permite realizar operaciones contra el sistema de archivos del equipo donde está corriendo el programa. Todas las operaciones tienen una versión sícrónica y una asincrónica. Las variantes sincrónicas no deberán ser utilizadas en entornos donde la carga de trabajo sea importante ya que bloquean la ejecución de todo el programa hasta ser completadas.

Las operaciones más comunes incluyen:

  • fs.readFile
  • fs.writeFile
  • fs.appendFile
  • fs.stat
  • fs.rename
  • fs.mkdir
  • fs.readdir
  • fs.rmdir

fs.readFile

Para leer un archivo de texto y mostrarlo en pantalla, se utiliza fs.readFile de la siguiente manera:

var fs = require("fs");
fs.readFile("archivo.txt", "utf8", function (err, data) {
    if (err) {
        throw err;
    }
    console.log(data);  
});

Si no se especifica la codificación del archivo a ser leído, readFile devuelve los datos crudos que representan el contenido del archivo. Los archivos de texto usan comunmente la codificación será utf8.

Ejercicios

Ejercicio 1

Crear un programa que simule el comando cat de Unix o type de Windows: debe recibir un nombre de archivo como parámetro e imprimir por pantalla el contenido del archivo.

Bonus: Crear un archivo bash o bat que simplifique la sintaxis a:

> node-cat mi-archivo.txt

Ejercicio 2

Crear un programa que, dada una ruta, imprima el nombre de todos los archivos de ese directorio.

Bonus: Si se recibe un parámetro adicional, por ejemplo txt, mostrar solo los archivos que tengan esa extensión.

Ejercicio 3

Crear un programa que renombre un archivo recibiendo dos parámetros: nombre actual y nuevo nombre. Adicionalmente, debe agregar el texto "// Archivo renombrado a [nuevo-nombre]" al final del mismo. Finalmente, debe imprimir por pantalla el nuevo contenido del archivo.

npm y el archivo package.json

npm, "Node Package Manager", es una herramienta que viene junto con Node.js y sirve principalmente para administrar los módulos que componen una aplicación JavaScropt. Sus principales componenetes son:

  • Herramineta npm: permite la ejecución de diferentes comandos para administrar módulos
  • Archivo package.json: contine datos sobre la configuración de la aplicación
  • Registro npmjs.org: almacena todos los módulos públicos existentes

El archivo package.json, entones, es una pieza fundamental en en entorno de programación de Node.js. Éste archivo contiene un objeto JSON (y no un objeto JavaScript) con información esencial sobre el programa o módulo que estamos desarrollando o utilizando, como así también sus dependencias y comandos para automatizar los ciclos de trabajo, entre otras cosas.

Si, por ejemplo, se desarrolla un programa basado en Express.js, en el archivo package.json debe a existir una entrada así:

{
    "dependencies": {
        "express": "^4.0.0"
    }
}

Lo anterior especifica que el programa depende del módulo express y que la versión requerida es cualquiera de la serie 4.x.

Una vez definidas las dependencias en el archivo, el comando npm install busca los módulos en el registro público y los instala localmente en la carpeta node_modules. A su vez, si un módulo depende de otros módulos, npm resolverá estas dependencias automáticamente. Finalmente, dentro del programa se podrá utilizar el módulo como si fuera uno de los módulos que vienen incluidos en Node.js:

var express = require("express");

Existe también la posibilidad de instalar y actualizar el archivo package.json en un solo paso utilizando la siguiente sintaxis:

> npm install --save express

El comando anterior buscará el módulo "express" en el registro, lo instalará localmente y actualizará el archivo package.json con el número de versión correspondiente.

Existen casos en los cuales un módulo no se utiliza durante el funcionamiento productivo del programa sino que es solo útil durante el desarrollo. Casos como éste son los módulos de automatización de pruebas como mocha. Utilizando la opción --save-dev, se instalará el módulo pero se incluirá en la sección devDependencies del package.json.

Las versiones de los paquetes siguen, usualmente, la convención "semver" o "Semantic Versioning". Esta convención numera las versiones con el formato MAYOR.MENOR.PARCHE, donde cada parte se incrementa si:

  • MAYOR: se incrementa cuando la nueva versión no es compatible con la anterior
  • MENOR: se incrementa cuando se agregan funcionalidades y se mantiene compatibilidad
  • PARCHE: se incrementa cuando se corrigen errores, sin agregar funcionalidad y manteniendo compatibilidad

En las dependencias definidas en el archivo package.json, se pueden especificar versiones exactas de módulos o también rangos. Por ejemplo:

  • 4.3.0: versión exacta igual a 4.3.0
  • ~4.3.0: cualquier versión 4.3.x
  • ^4.3.0: cualquier versión entre 4.3.0 y anterior a 5.x

Otras propiedades o secciones del archivo package.json que son muy utilizadas son las siguientes:

  • name: nombre del módulo o aplicación, que debe ser único si se va a publicar
  • version: versión del módulo o aplicación, siguiendo en lo posible la convención semver
  • description: breve descripción del módulo o aplicación, en una línea
  • scripts: define comandos que se pueden correr para automatizar distintas tareas de desarrollo
  • private: si el módulo es privado y esta propiedad es true, evita una publicación accidental

Recursos complementarios

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment