035.1 Lección 1
Certificación: |
Conceptos básicos de desarrollo web |
---|---|
Versión: |
1.0 |
Tema: |
035 Programación NodeJS server |
Objetivo: |
035.1 Conceptos básicos de Node.js |
Lección: |
1 de 1 |
Introducción
Node.js es un entorno de tiempo de ejecución de JavaScript que ejecuta código JavaScript en servidores web, el llamado backend web (lado del servidor), en lugar de usar un segundo lenguaje como Python o Ruby para programas del lado del servidor. El lenguaje JavaScript ya se usa en la parte frontal moderna de las aplicaciones web, interactuando con el HTML y CSS de la interfaz que el usuario interactúa con un navegador web. El uso de Node.js junto con JavaScript en el navegador ofrece la posibilidad de un solo lenguaje de programación para toda la aplicación.
La razón principal de la existencia de Node.js es la forma en que maneja múltiples conexiones concurrentes en el backend. Una de las formas más comunes en que un servidor de aplicaciones web maneja las conexiones es mediante la ejecución de múltiples procesos. Cuando abre una aplicación de escritorio en su computadora, se inicia un proceso y utiliza muchos recursos. Ahora piense en miles de usuarios haciendo lo mismo en una gran aplicación web.
Node.js evita este problema mediante un diseño llamado event loop, que es un ciclo interno que comprueba continuamente si se calculan las tareas entrantes. Gracias al uso generalizado de JavaScript y la ubicuidad de las tecnologías web, Node.js ha tenido una gran adopción tanto en aplicaciones pequeñas como grandes. Hay otras características que también ayudaron a que Node.js se adoptara ampliamente, como el procesamiento de entrada/salida (E/S) asincrónico y sin bloqueo, que se explica más adelante en esta lección.
El entorno Node.js utiliza un motor JavaScript para interpretar y ejecutar código JavaScript en el servidor o en el escritorio. En estas condiciones, el código JavaScript que escribe el programador se analiza y compila justo a tiempo para ejecutar las instrucciones de la máquina generadas por el código JavaScript original.
Note
|
A medida que avanza en estas lecciones sobre Node.js, puede notar que el JavaScript de Node.js no es exactamente el mismo que el que se ejecuta en el navegador (que sigue a ECMAScript specification), pero es bastante similar. |
Empezando
Esta sección y los siguientes ejemplos asumen que Node.js ya está instalado en su sistema operativo Linux y que el usuario ya tiene habilidades básicas como ejecutar comandos en la terminal.
Para ejecutar los siguientes ejemplos, cree un directorio de trabajo llamado node_examples
. Abra un indicador de terminal y escriba node
. Si ha instalado correctamente Node.js, presentará un mensaje >
donde puede probar los comandos de JavaScript de forma interactiva. Este tipo de entorno se llama REPL, por “leer, evaluar, imprimir y repetir”. Escriba la siguiente entrada (o algunas otras declaraciones de JavaScript) en las indicaciones >
. Presione la tecla Enter después de cada línea, y el entorno REPL devolverá los resultados de sus acciones:
> let array = ['a', 'b', 'c', 'd']; undefined > array.map( (element, index) => (`Element: ${element} at index: ${index}`)); [ 'Element: a at index: 0', 'Element: b at index: 1', 'Element: c at index: 2', 'Element: d at index: 3' ] >
El fragmento se escribió utilizando la sintaxis ES6, que ofrece una función de mapa para iterar sobre el arreglo e imprimir los resultados utilizando plantillas de cadenas. Puede escribir prácticamente cualquier comando que sea válido. Para salir de la terminal de Node.js, escriba .exit
, recordando incluir el periodo inicial.
Para scripts y módulos más largos, es más conveniente usar un editor de texto como VS Code, Emacs o Vim. Puede guardar las dos líneas de código que se acaban de mostrar (con una pequeña modificación) en un archivo llamado start.js
:
let array = ['a', 'b', 'c', 'd'];
array.map( (element, index) => ( console.log(`Element: ${element} at index: ${index}`)));
Luego, puede ejecutar el script desde el shell para producir los mismos resultados que antes:
$ node ./start.js Element: a at index: 0 Element: b at index: 1 Element: c at index: 2 Element: d at index: 3
Antes de sumergirnos en más código, vamos a obtener una descripción general de cómo funciona Node.js, utilizando su entorno de ejecución de un solo hilo y el bucle de eventos.
Bucle de eventos y subproceso único
Es difícil saber cuánto tiempo le tomará a su programa Node.js manejar una solicitud. Algunas solicitudes pueden ser breves, tal vez simplemente recorrer variables en la memoria y devolverlas, mientras que otras pueden requerir actividades que requieren mucho tiempo, como abrir un archivo en el sistema o emitir una consulta a una base de datos y esperar los resultados. ¿Cómo maneja Node.js esta incertidumbre? El ciclo de eventos es la respuesta.
Imagine a un chef realizando múltiples tareas. Hornear un pastel es una tarea que requiere mucho tiempo para que el horno lo cocine. El chef no se queda allí esperando a que el pastel esté listo y luego se pone a preparar un café. En cambio, mientras el horno hornea el pastel, el chef prepara café y otras tareas en paralelo. Pero el cocinero siempre está comprobando si es el momento adecuado para centrarse en una tarea específica (preparar café) o para sacar el pastel del horno.
El ciclo del evento es como el chef que está constantemente al tanto de las actividades que lo rodean. En Node.js, un “event-checker” siempre está buscando operaciones que se hayan completado o estén esperando ser procesadas por el motor de JavaScript.
Con este enfoque, una operación asincrónica y larga no bloquea otras operaciones rápidas posteriores. Esto se debe a que el mecanismo de bucle de eventos siempre verifica si esa tarea larga, como una operación de E/S, ya está hecha. De lo contrario, Node.js puede continuar procesando otras tareas. Una vez que se completa la tarea en segundo plano, se devuelven los resultados y la aplicación de Node.js puede usar una función de activación (callback) para procesar aún más la salida.
Debido a que Node.js evita el uso de múltiples subprocesos, como hacen otros entornos, se denomina entorno de un solo subproceso y, por lo tanto, un enfoque sin bloqueo es de suma importancia. Es por eso que Node.js usa un bucle de eventos. Sin embargo, para las tareas de procesamiento intensivo, Node.js no se encuentra entre las mejores herramientas: existen otros lenguajes y entornos de programación que abordan estos problemas de manera más eficiente.
En las siguientes secciones, veremos más de cerca las funciones de devolución de llamada. Por ahora, comprenda que las funciones de devolución de llamada son activadores que se ejecutan al completar una operación predefinida.
Módulos
Es una buena práctica dividir la funcionalidad compleja y los fragmentos extensos de código en partes más pequeñas. Hacer esta modularización ayuda a organizar mejor el código base, abstraer las implementaciones y evitar problemas de ingeniería complicados. Para satisfacer esas necesidades, los programadores empaquetan bloques de código fuente para ser consumidos por otras partes internas o externas del código.
Considere el ejemplo de un programa que calcula el volumen de una esfera. Abra su editor de texto y cree un archivo llamado volumeCalculator.js
que contenga el siguiente JavaScript:
const sphereVol = (radius) => {
return 4 / 3 * Math.PI * radius
}
console.log(`A sphere with radius 3 has a ${sphereVol(3)} volume.`);
console.log(`A sphere with radius 6 has a ${sphereVol(6)} volume.`);
Ahora, ejecute el archivo usando Node:
$ node volumeCalculator.js A sphere with radius 3 has a 113.09733552923254 volume. A sphere with radius 6 has a 904.7786842338603 volume.
Aquí, se utilizó una función simple para calcular el volumen de una esfera, en función de su radio. Imagine que también necesitamos calcular el volumen de un cilindro, cono, etc.: notamos rápidamente que esas funciones específicas deben agregarse al archivo volumeCalculator.js
, que puede convertirse en una enorme colección de funciones. Para organizar mejor la estructura, podemos usar los módulos como paquetes de código separado.
Para hacer eso, cree un archivo separado llamado polyhedrons.js
:
const coneVol = (radius, height) => {
return 1 / 3 * Math.PI * Math.pow(radius, 2) * height;
}
const cylinderVol = (radius, height) => {
return Math.PI * Math.pow(radius, 2) * height;
}
const sphereVol = (radius) => {
return 4 / 3 * Math.PI * Math.pow(radius, 3);
}
module.exports = {
coneVol,
cylinderVol,
sphereVol
}
Ahora, en el archivo volumeCalculator.js
, elimine el código antiguo y reemplácelo con este fragmento:
const polyhedrons = require('./polyhedrons.js');
console.log(`A sphere with radius 3 has a ${polyhedrons.sphereVol(3)} volume.`);
console.log(`A cylinder with radius 3 and height 5 has a ${polyhedrons.cylinderVol(3, 5)} volume.`);
console.log(`A cone with radius 3 and height 5 has a ${polyhedrons.coneVol(3, 5)} volume.`);
Y luego ejecute el nombre del archivo en el entorno Node.js:
$ node volumeCalculator.js A sphere with radius 3 has a 113.09733552923254 volume. A cylinder with radius 3 and height 5 has a 141.3716694115407 volume. A cone with radius 3 and height 5 has a 47.12388980384689 volume.
En el entorno Node.js, cada archivo de código fuente se considera un módulo, pero la palabra “module” en Node.js indica código empaquetado como en el ejemplo anterior. Mediante el uso de módulos, extrajimos las funciones de volumen del archivo principal, volumeCalculator.js
, reduciendo así su tamaño y facilitando la aplicación de pruebas unitarias, que son una buena práctica al desarrollar aplicaciones del mundo real.
Ahora que sabemos cómo se usan los módulos en Node.js, podemos usar una de las herramientas más importantes: el Node Package Manager (NPM).
Uno de los principales trabajos de NPM es administrar, descargar e instalar módulos externos en el proyecto o en el sistema operativo. Puede inicializar un repositorio de nodos con el comando npm init
.
NPM hará las preguntas predeterminadas sobre el nombre de su repositorio, versión, descripción, etc. Puede omitir estos pasos usando npm init --yes
, y el comando generará automáticamente un archivo package.json
que describe las propiedades de su proyecto/módulo.
Abra el archivo package.json
en su editor de texto favorito y verá un archivo JSON que contiene propiedades como palabras clave, comandos de script para usar con NPM, un nombre, etc.
Una de esas propiedades son las dependencias que están instaladas en su repositorio local. NPM agregará el nombre y la versión de estas dependencias en package.json
, junto con package-lock.json
, otro archivo utilizado como respaldo por NPM en caso de que package.json
falle.
Escriba lo siguiente en su terminal:
$ npm i dayjs
La bandera i
es un atajo para el argumento install
. Si está conectado a Internet, NPM buscará un módulo llamado dayjs
en el repositorio remoto de Node.js, descargará el módulo e instalará localmente. NPM también agregará esta dependencia a sus archivos package.json
y package-lock.json
. Ahora puede ver que hay una carpeta llamada node_modules
, que contiene el módulo instalado junto con otros módulos si son necesarios. El directorio node_modules
contiene el código real que se utilizará cuando se importe y se llame a la biblioteca. Sin embargo, esta carpeta no se guarda en los sistemas de control de versiones que utilizan Git, ya que el archivo package.json
proporciona todas las dependencias utilizadas. Otro usuario puede tomar el archivo package.json
y simplemente ejecutar npm install
en su propia máquina, donde NPM creará una carpeta node_modules
con todas las dependencias del package.json
, evitando así el control de versiones para el miles de archivos disponibles en el repositorio de NPM.
Ahora que el módulo dayjs
está instalado en el directorio local, abra la consola de Node.js y escriba las siguientes líneas:
const dayjs = require('dayjs');
dayjs().format('YYYY MM-DDTHH:mm:ss')
El módulo dayjs
se carga con la palabra clave require
. Cuando se llama a un método del módulo, la biblioteca toma la fecha y hora del sistema actual y la genera en el formato especificado:
2020 11-22T11:04:36
Este es el mismo mecanismo utilizado en el ejemplo anterior, donde el tiempo de ejecución de Node.js carga la función de terceros en el código.
Funcionalidad del servidor
Debido a que Node.js controla el back-end de las aplicaciones web, una de sus tareas principales es manejar las solicitudes HTTP.
A continuación, se muestra un resumen de cómo los servidores web manejan las solicitudes HTTP entrantes. La funcionalidad del servidor es escuchar solicitudes, determinar lo más rápido posible qué respuesta necesita cada uno y devolver esa respuesta al remitente de la solicitud. Esta aplicación debe recibir una solicitud HTTP entrante desencadenada por el usuario, analizar la solicitud, realizar el cálculo, generar la respuesta y devolverla. Se utiliza un módulo HTTP como Node.js porque simplifica esos pasos, permitiendo que un programador web se concentre en la aplicación en sí.
Considere el siguiente ejemplo que implementa esta funcionalidad muy básica:
const http = require('http');
const url = require('url');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
const queryObject = url.parse(req.url,true).query;
let result = parseInt(queryObject.a) + parseInt(queryObject.b);
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end(`Result: ${result}\n`);
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
Guarde estos contenidos en un archivo llamado basic_server.js
y ejecútelo a través de un comando node
. La terminal que ejecuta Node.js mostrará el siguiente mensaje:
Server running at http://127.0.0.1:3000/
Luego, visite la siguiente URL en su navegador web: http://127.0.0.1:3000/numbers?a=2&b=17
Node.js está ejecutando un servidor web en su computadora y usa dos módulos: http
y url
. El módulo http
configura un servidor HTTP básico, procesa las solicitudes web entrantes y las entrega a nuestro código de aplicación simple. El módulo de URL analiza los argumentos pasados en la URL, los convierte a un formato numérico entero (integer) y realiza la operación de suma. El módulo http
envía la respuesta como texto al navegador web.
En una aplicación web real, Node.js se usa comúnmente para procesar y recuperar datos, generalmente de una base de datos, y devolver la información procesada al front-end para mostrarla. Pero la aplicación básica de esta lección muestra de manera concisa cómo Node.js utiliza módulos para manejar solicitudes web como un servidor web.
Ejercicios guiados
-
¿Cuáles son las razones para usar módulos en lugar de escribir funciones simples?
-
¿Por qué el entorno Node.js se volvió tan popular? Cite una característica.
-
¿Cuál es el propósito del archivo
package.json
? -
¿Por qué no se recomienda guardar y compartir la carpeta
node_modules
?
Ejercicios Exploratorios
-
¿Cómo puede ejecutar aplicaciones Node.js en su computadora?
-
¿Cómo se pueden delimitar los parámetros en la URL para analizar dentro del servidor?
-
Especifique un escenario en el que una tarea específica podría ser un cuello de botella para una aplicación Node.js.
-
¿Cómo implementaría un parámetro para multiplicar o sumar los dos números en el ejemplo del servidor?
Resumen
Esta lección proporcionó una descripción general del entorno Node.js, sus características y cómo se puede usar para implementar programas simples. Esta lección incluye los siguientes conceptos:
-
Qué es Node.js y por qué se usa.
-
Cómo ejecutar programas Node.js usando la línea de comandos.
-
El evento
loops
y el hilo único (single thread
). -
Módulos.
-
Node Package Manager (NPM).
-
Funcionalidad del servidor.
Respuestas a los ejercicios guiados
-
¿Cuáles son las razones para usar módulos en lugar de escribir funciones simples?
Al optar por módulos en lugar de funciones convencionales, el programador crea una base de código más simple para leer y mantener y para la cual escribir pruebas automatizadas.
-
¿Por qué el entorno Node.js se volvió tan popular? Cite dos características.
Una de las razones es la flexibilidad del lenguaje JavaScript, que ya se usaba ampliamente en el front-end de las aplicaciones web. Node.js permite el uso de un solo lenguaje de programación en todo el sistema.
-
¿Cuál es el propósito del archivo
package.json
?Este archivo contiene metadatos para el proyecto, como el nombre, la versión, las dependencias (bibliotecas), etc. Dado un archivo
package.json
, otras personas pueden descargar e instalar las mismas bibliotecas y ejecutar pruebas de la misma manera que lo hizo el creador original. -
¿Por qué no se recomienda guardar y compartir la carpeta
node_modules
?La carpeta
node_modules
contiene las implementaciones de bibliotecas disponibles en repositorios remotos. Entonces, la mejor manera de compartir estas bibliotecas es indicándolas en el archivopackage.json
y luego usar NPM para descargar esas bibliotecas. Este método es más simple y sin errores, ya que no es necesario realizar un seguimiento ni mantener las bibliotecas localmente.
Respuestas a los ejercicios de exploración
-
¿Cómo puede ejecutar aplicaciones Node.js en su computadora?
Puede ejecutarlos escribiendo
node PATH/FILE_NAME.js
en la línea de comandos de su terminal, cambiandoPATH
por la ruta de su archivo Node.js y cambiandoFILE_NAME.js
por el nombre de archivo elegido. -
¿Cómo se pueden delimitar los parámetros en la URL para analizar dentro del servidor?
El caracter
&
se utiliza para delimitar esos parámetros, de modo que se puedan extraer y analizar en el código JavaScript. -
Especifique un escenario en el que una tarea específica podría ser un cuello de botella para una aplicación Node.js.
Node.js no es un buen entorno para ejecutar procesos intensivos de CPU porque utiliza un solo hilo. Un escenario de cálculo numérico podría ralentizar y bloquear toda la aplicación. Si se necesita una simulación numérica, es mejor utilizar otras herramientas.
-
¿Cómo implementaría un parámetro para multiplicar o sumar los dos números en el ejemplo del servidor?
Use un operador ternario o una condición if-else para verificar un parámetro adicional. Si el parámetro es la cadena
mult
devuelve el producto de los números, de lo contrario devuelve la suma. Reemplace el código anterior con el fragmento a continuación. Reinicie el servidor en la línea de comandos presionando kbd:[Ctrl+C] y vuelva a ejecutar el comando para reiniciar el servidor. Ahora pruebe la nueva aplicación visitando la URLhttp://127.0.0.1:3000/numbers?a=2&b=17&operation=mult
en su navegador. Si omite o cambia el último parámetro, los resultados deben ser la suma de los números.let result = queryObject.operation == 'mult' ? parseInt(queryObject.a) * parseInt(queryObject.b) : parseInt(queryObject.a) + parseInt(queryObject.b);