Skip to content

Latest commit

 

History

History
814 lines (667 loc) · 31.5 KB

File metadata and controls

814 lines (667 loc) · 31.5 KB

shieldsIO shieldsIO shieldsIO

WideImg

Máster en Programación FullStack con JavaScript y Node.js

JS, Node.js, Frontend, Backend, Firebase, Express, Patrones, HTML5_APIs, Asincronía, Websockets, Testing

Clase 49

IMG

Modularización

  • Especificación de CommonJS
  • Exports es un objeto que vamos "rellenando"
  • La asignacion al exports es inmediata. No se pueden usar callbacks o similares
  • No es necesario usar module.exports ya es que es global.
    • const exports = module.exports = {};
  • Es importante controlar la reasignación de module.exports

Modularización: Usando exports

  • Exportar los datos:
// archivo -> config.js

const datoPrivado = "Lo que pasa en Node... se queda en Node";
const datoCompartido = "Hola! desde Config.js"

function privada (){
	return datoPrivado;
}

exports.metodo = function () {
	console.log(datoCompartido);
	console.log(privada());
}
exports.mensaje = datoCompartido;
  • Importar los datos:
const config = require('./config');

config.metodo();
console.log(config.mensaje);

Modularización: Usando module.exports

Exportar los datos:

// archivo -> config.js
const config = {
  token: "<--- MiSecreto--->",
};

module.exports = config;

Importar los datos:

const config = require('./config');

console.log(config.token);

Librerías Nativas en v10 (LTS)

Changelogs v10.x

El indice de estabilidad actual

  • 0 - Deprecated This feature is known to be problematic, and changes may be planned. Do not rely on it. Use of the feature may cause warnings to be emitted. Backwards compatibility across major versions should not be expected
  • 1 - Experimental This feature is still under active development and subject to non-backwards compatible changes, or even removal, in any future version. Use of the feature is not recommended in production environments. Experimental features are not subject to the Node.js Semantic Versioning model
  • 2 - Stable The API has proven satisfactory. Compatibility with the npm ecosystem is a high priority, and will not be broken unless absolutely necessary.

Los estados originales

  • 0 Deprecated
  • 1 Experimental
  • 2 Unstable
  • 3 Stable
  • 4 API Frozen
  • 5 Locked

Librerías Nativas en v10 (LTS): Librerías Estables (status 2)

  • Assertion Testing - Librería de testing
  • Buffer - Permite el trabajo con datos crudos
  • C/C++ Addons - N-API - N-API: Next generation Node.js APIs for native modules
  • Child Processes - Permite crear y gestionar "procesos hijo"
  • Cluster - Permite gestionar nuestro proceso principal e "hijos" entre diversos módulos
  • Console - Permite trabajar con la consola (terminal), imitando la consola del navegador
  • Crypto - Relacionado a las funcionalidades de criptografía necesarias para algunos protocolos como SSL, Hashes, firmas...
  • Debugger - Utilidades de depuración para utilizar el inspector del V8
  • DNS - Gestion y resolución de nombres de Dominios
  • Events - Permite gestionar y crear eventos
  • File System - Permite manipular y crear ficheros en el sistema
  • HTTP - Gestión del protocolo HTTP (Peticiones y respuestas)
  • HTTPS - Gestión del protocolo HTTPS (http y tls/ssl) con peticiones y respuestas
  • Modules - Gestión y carga de módulos (require() y exports)
  • Net - Nos aporta una capa de red asíncrona y permite gestionar "streams" tanto cliente como servidor sobre TCP/IP
  • OS - Información básica sobre el sistema operativo en el que estamos funcionando
  • Path - Gestión de rutas dentro del sistema (navegación de carpetas y archivos)
  • Query Strings - Manipualción y gestion de cadenas URL
  • Readline - Gestiona entrada de datos interactiva por la terminal (preguntas/respuestas)
  • REPL - Una terminal interactiva del estilo Read-Eval-Print-Loop (REPL)
  • Stream - Interfaz abstracta usada por otros módulos para gestionar el flujo de la información
  • [String Decoder] - Permite decodificar objetos tipo buffer a cadenas de texto UTF-8 y UTF-16
  • Timers - Funciones globales de tiempo como setInterval(), clearInterval(), etc...
  • TLS/SSL - Capa de encriptación basada en OpenSSL
  • TTY - Manejo interno de lectura y escritura de streams
  • UDP/Datagram - Implementación de de UDP Datagram sockets
  • URL - Facilita la resolución y parseo de URLs
  • Utilities - Utilidades varias que usa internamente Nodejs, la mayoría depreciadas
  • VM - Permite aislar código en "sandboxes" y utilizar Maquinas virtuales de JavaScript
  • ZLIB - Permite trabajar con Gzip/Gunzip, Deflate/Inflate y DeflateRaw/InflateRaw

Librerías Nativas en v10 (LTS): Librerías Experimentales (status 1)

  • Async Hooks - Trackea el ciclo de vida de los callbacks
  • ECMAScript Modules - Utilización de módulso de es6 (Import/Export)
  • HTTP/2 - Implementación experimental del protocolo http2
  • Inspector - Una API para trabajar con el con el Inspector del V8
  • Trace Events - Facilita un mecanismo que centraliza la información del v8, node core...
  • Worker Threads - Proporciona una forma de crear múltiples entornos que se ejecutan en subprocesos independientes con canales de comunicación entre ellos.

Librerías Nativas en v10 (LTS): Librerías Deprecadas (status 0)

Librerías Nativas en v10 (LTS): Librerías Especiales

Librerías Nativas: Debugger

  • No es un debugger completo
  • Es muy tosco y manual
  • No cuenta con GUI
  • La mejor alternativa es usar otras herramientas

node inspect myscript.js

Recursos

Librerías Nativas: Assertion

  • Cuenta con un modo estricto de igualdad
  • Es muy sencillo
  • Se orienta exclusivamente a pruebas de tipo unitario
  • Existen librerías más potentes

Métodos

Básico

const assert = require('assert');

assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]);
assert.deepStrictEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]);

Modo Estricto

const assert = require('assert').strict;

// Ahora deepEqual() es lo mismo que deepStrictEqual()
assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]);

Detalles del error

const assert = require('assert');

// Generate an AssertionError to compare the error message later:
const { message } = new assert.AssertionError({
  actual: 1,
  expected: 2,
  operator: 'strictEqual'
});

// Verify error output:
try {
  assert.strictEqual(1, 2);
} catch (err) {
  assert(err instanceof assert.AssertionError);
  assert.strictEqual(err.message, message);
  assert.strictEqual(err.name, 'AssertionError [ERR_ASSERTION]');
  assert.strictEqual(err.actual, 1);
  assert.strictEqual(err.expected, 2);
  assert.strictEqual(err.code, 'ERR_ASSERTION');
  assert.strictEqual(err.operator, 'strictEqual');
  assert.strictEqual(err.generatedMessage, true);
}

Recursos

Librerías Nativas: HTTP

Hello World con HTTP:

//import http from 'http';
const http = require('http');

const puerto = 3000;
const direccion = "127.0.0.1";
const mensaje = 'Hola a todos, ahora usando HTTP\n';


http.createServer((req, res) => {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end(mensaje);
}).listen(puerto, direccion);
console.log(`Server running at http://${direccion}:${puerto}/`);

Hello World desde c9.io:

//import http from 'http';
const http = require('http');

const mensaje = 'Hola a todos, ahora usando HTTP con C9.io\n';

http.createServer((req, res) => {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end(mensaje);
}).listen(process.env.PORT, process.env.IP);

console.log(`Server running at http://${process.env.IP}:${process.env.PORT}/`);

Rediccionamiento:

//import http from 'http';
const http = require('http');

http.createServer((req, res) => {
  res.writeHead(301, {
    'Location': 'http://www.fictizia.com/'
  });
    res.end();
}).listen(process.env.PORT, process.env.IP);

console.log(`Servidor funcionando en http://${process.env.IP}:${process.env.PORT}/`);

Ping (petición http):

//import http from 'http';
const http = require('http');

var url = "google.es";

http.get({ host: url }, resOrigen => {
    http.createServer((req, res) => {
        res.writeHead(200, {'Content-Type': 'text/plain'});
        res.end(`La respuesta de ${url} es ${resOrigen.statusCode}` );
        console.log(`La respuesta de ${url} es ${resOrigen.statusCode}` );
    }).listen(process.env.PORT, process.env.IP);
    console.log(`Servidor disponible en http://${process.env.IP}:${process.env.PORT}/`);
}).on('error', e => {
    http.createServer((req, res) => {
        res.writeHead(200, {'Content-Type': 'text/plain'});
        res.end(`La respuesta de ${url} genera un error - ${e.message}`  );
    }).listen(process.env.PORT, process.env.IP);
    console.log(`Servidor disponible en http://${process.env.IP}:${process.env.PORT}/`);
    console.log(`Tenemos un error!! - ${e.message}`);
});

Recursos

Librerías Nativas: URL

Leyendo urls:

//import url from 'url';
const url = require('url');

const demoURL = "http://localhost:3000/ruta?parametro=dato#detalle";

console.log(`El host: ${url.parse(demoURL).hostname}
El puerto: ${url.parse(demoURL).port}
La ruta: ${url.parse(demoURL).pathname}
La parametro: ${url.parse(demoURL).query}
El hash(#): ${url.parse(demoURL).hash}`);

Librerías Nativas: HTTP Avanzado

Trabajando con rutas

/*
import http from 'http';
import url from 'url';
*/

const http = require('http'),
    url = require('url');

http.createServer((req, res) => {
  const pathname = url.parse(req.url).pathname;
  if (pathname === '/') {
      res.writeHead(200, {
      'Content-Type': 'text/plain'
    });
    res.end('Index!');
  } else if (pathname === '/otro') {
      res.writeHead(200, {
      'Content-Type': 'text/plain; charset=utf-8'
    });
    res.end('sencillamente otra página');
  } else if (pathname === '/alindex') {
      res.writeHead(301, {
      'Location': '/'
    });
    res.end();    
  } else {
      res.writeHead(404, {
      'Content-Type': 'text/plain'
    });
    res.end('Querido... 404!');
  }
}).listen(process.env.PORT, process.env.IP);
console.log(`Servidor funcionando en http://${process.env.IP}:${process.env.PORT}/`);

Ping recurrente por consola

// import http from 'http';
const http = require('http');
const url = "fictizia.es";
const tiempo = 3500;

setInterval(() => {
    http.get({ host: url }, res => {
        if (res.statusCode === 200 ) {  
            console.log(`Sin errores en ${url}`);
        } else {
            console.log(`Respuesta Http ${res.statusCode} en ${url}`);
        }    
    }).on('error', e => {
            console.log(`Con errores -> La respuesta de ${url} es ${e.message}`  );
    });
}, tiempo);

Librerías Nativas: La asincronía es esencial

El modelo de programación de Node.js es monohilo, asíncrono y dirigido por eventos.

  1. No puede haber código bloqueante o todo el servidor quedará bloqueado y esto incluye no responder a nuevas peticiones entrantes.
  2. La asincronicidad implica que no sabemos cuándo ni en que orden se va a ejecutar el código, generalmente esto no es importante pero en ocasiones sí lo es y habrá que tenerlo en cuenta.
  3. En caso de error inesperado debemos capturarlo y controlar el posible estado en que haya podido quedar la ejecución del código.

Nicolas Nombela en nnombela

Sincrónico - código bloqueante

//import http from "http";
const http = require("http");

let numeroPeticion = 1;
function writeResponse(response) {
    response.writeHead(200, {"Content-Type": "text/plain"});
    response.write(`Hola Mundo!, numero de peticion ${numeroPeticion}`);
    response.end();
    console.log(`Se termino... ${numeroPeticion}`);
}

function sleepSynch(seconds, response) {
    const startTime = new Date().getTime();
    while (new Date().getTime() < startTime + Math.floor((Math.random() * 1000) + 500) * seconds) {
        // Nada pasa....
    }
    writeResponse(response);
}

http.createServer((request, response) => {
    console.log(`Empezo... ${numeroPeticion}`);
    sleepSynch(10, response);
    numeroPeticion++;
}).listen(process.env.PORT);

console.log(`en puerto ->${process.env.PORT}`);

Asincronico - timeOut()

//import http from "http";
const http = require("http");

function writeResponse(response) {
    response.writeHead(200, {"Content-Type": "text/plain"});
    response.write("Hola Mundo!");
    response.end();
    console.log("Se termino... ");
}

function sleepAsynch(seconds, response) {
    setTimeout(() => {
    writeResponse(response);
    }, Math.floor((Math.random() * 1000) + 100) * seconds);
}

http.createServer((request, response) => {

    console.log("Empezo... ");
    sleepAsynch(10, response);
}).listen(process.env.PORT);

console.log(`en puerto ->${process.env.PORT}`);

Librerías Nativas: Mantener Node funcionando

Se cierra

setTimeout(() => {
  process.stdout.write("Cerrando Node...")
}, 1000)

Se mantiene

setInterval(() => {
  // Nada pasa... pero sigo funcionando
}, 1000)

Programando una salida

let contador = 0;
setInterval(() => {
  contador++
  if(contador > 10){
    process.exit()
  } else {
      console.log(`Sigo. Valor contador -> ${contador}\n`)
  }
}, 1000);

Librerías Nativas: File System

Leer un archivo

//import fs from 'fs';
const fs = require('fs');

fs.readFile('archivo.txt', 'utf8', (err, data) => {
    if (!err) {
      console.log(data);
    } else {
      throw err;
    }
});

Escribir un archivo

//import fs from 'fs';
const fs = require('fs');

const data = "y esto... se guardará en el archivo.txt";
fs.writeFile('archivo.txt', data, err => {
  if (!err) {
    console.log('Datos guardados en el archivo.txt');
  } else {
    throw err;
  }
});

Usando Promesas y Callbacks

//import fs from 'fs';
const fs = require('fs');

// Con CallBacks!
fs.readFile("./miArchivo.txt", (error, content) => {
	console.log("Leyendo el archivo...");
	fs.writeFile("./logitud.txt", content.length, error => {
	    if (error) {
	        console.log("error! ", error);
	    } else {
		    console.log("Terminado... hemos almacenado una cadena que vale ",content.length);
	    }
	});
});

// Con Promesas!
function leerArchivo (nombre) {
	return new Promise((resolver, rechazar) => {
		fs.readFile(nombre, (error, contenido) => {
			console.log("Empezando la lectura de ", nombre);
			if(error){
				console.log("Error en la lectura");
				return rechazar(error);
			}
				console.log("Lectura finalizada en ", nombre);
				resolver(contenido);
		});
	});
}

function escribirArchivo (nombre, contenido){
	return new Promise((resolver, rechazar) => {
		fs.writeFile(nombre, contenido, error => {
			if(error){
				console.log("Error en la escritura de ", nombre);
				rechazar(error);
			} else {
				console.log("Escritura Termianda en ", nombre);
				resolver();
			}
		});
	});
}

//Opción1
leerArchivo("./miArchivo.txt")
.then(contenido => {
    escribirArchivo("./longitud.txt", contenido);
})
.catch(error => {
	console.log("Promesas con errores: ");
	console.log(error);
});

//Opción2
Promise.all([
	leerArchivo("./otros.txt"),
	leerArchivo("./usuarios.txt"),
	leerArchivo("./mas_cosas.txt")
	]).then(respuestas => {
		console.log(`Tenemos un total de ${respuestas.length} respuesta/s.`);
		console.log(`El primero tiene ${respuestas[0].length} caracteres`);
		console.log(`El segundo tiene ${respuestas[1].length} caracteres`);
		console.log(`El tercero tiene ${respuestas[2].length} caracteres`);
	});


//Opcion3
Promise.race([
	leerArchivo("./otros.txt"),
	leerArchivo("./usuarios.txt"),
	leerArchivo("./mas_cosas.txt")
	]).then(respuesta => {
		console.log(`El más rápido tiene solo ${respuesta.length} caracteres.`);
	});

Más métodos para:

  • Síncronos
  • Escucha de cambios
  • Manipulación de carpetas
  • etc...

Recursos

Librerías Nativas: Events

events

  • Patrón Observador
  • Similar al navegador

Suscribiendonos a eventos

  • Sin eventos
//import http from 'http';
const http = require('http');

const server = http.createServer((request, response) => {
	response.writeHead(200, {"Content-Type": "text/plain"});
	response.end("¡Hola people!");
}).listen(process.env.PORT);

console.log(`Servidor escuchando por el puerto ${process.env.PORT}`);
  • Con eventos
//import http from 'http';
const http = require('http');

const server = http.createServer().listen(process.env.PORT);

server.on('request', (request, response) => {
	response.writeHead(200, {"Content-Type": "text/plain"});
	response.end("¡Hola people!");
});

console.log(`Servidor escuchando por el puerto ${process.env.PORT}`);

Ejemplo sencillo: Creando nuestros eventos

//import eventos from 'events';
const eventos = require('events');

const EmisorEventos = eventos.EventEmitter;
const ee = new EmisorEventos();

ee.on('datos', fecha => {
   console.log(fecha);
});

setInterval(() => {
   ee.emit('datos', Date.now());
}, 500);

Recursos

Librerías Nativas: Juguemos al Ping-Pong

img

//import {EventEmitter} from 'events';
const EventEmitter = require('events').EventEmitter;

const pingPong = new EventEmitter();

let pingNumero = 1;

console.log('Bienvenido al juego de Ping/Pong!');
console.log('Empezamos en 5 segundos....');

setTimeout(() => {
  console.log('Primer Ping... que desencadenará el juego');
  pingPong.emit('ping', pingNumero);
  pingNumero++;
}, 5000);

pingPong.on('ping', numero => {
  console.log(`Llegó el Ping(${numero}). Emitimos Pong`);
  setTimeout(() => {
    pingPong.emit('pong');
  }, 1000);
});

pingPong.on('pong', () => {
  console.log('Llegó el Pong. Emitimos Ping');
  setTimeout(() => {
    pingPong.emit('ping', pingNumero);
    pingNumero++;
  }, 1000);
});

const pingLogger = numero => {
  console.log(`Llegó el Ping (${numero}) al nuevo listener`);
};

setTimeout(() => {
  console.log('Añadiendo un nuevo listener a Ping');
  pingPong.on('ping', pingLogger);
}, 10000);

setTimeout(() => {
  console.log('Eliminando el último listener');
  pingPong.removeListener('ping', pingLogger);
}, 12000);

console.log('Nota: Recuerda que los Eventos nos ayudan con la asincronía, ¿no?');

Ejercicios

1 - Crea las rutas básicas para tener una página web clásica (¿Quienes somos? | ¿Donde Estamos? | ¿Que hacemos? | Contacto... etc...)

// solución aquí

2 - Realiza un script ejecutable que nos muestre la información de los terremotos acontecidos en la última hora.

  • Fuente de datos
  • Ejemplo llamada JSON
  • Requisitos:
    • Debemos utilizar párametros cuando se ejecute para definir la magnitud de los terremotos que queremos
    • Si no se detecta el parámetro... la aplicación debe cerrarse.
    • Si el parametro es incorrecto también.
    • Ajustaremos la petición http en función del parámetro.
  • Apariencia(Orientativa):
*****************************
USGS All Earthquakes, Past Hour
   ---------------------     
total: 8
status: 200
   ---------------------     
5/10/2016, 3:46:30 PM
==============================
M 1.3 - 6km WNW of Anza, California
5/10/2016, 3:43:01 PM
Magnitud: 1.32
Estatus: automatic
Tipo: earthquake
Lugar: 6km WNW of Anza, California
Coordenadas: -116.7246704 , 33.5830002
Info: http://earthquake.usgs.gov/earthquakes/eventpage/ci37563240
Detalles: http://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/ci37563240.geojson
==============================
... (por cada terremoto de los iguales a los iguales)
// Solución aquí