Skip to content

Latest commit

 

History

History
518 lines (419 loc) · 13.6 KB

File metadata and controls

518 lines (419 loc) · 13.6 KB

shieldsIO shieldsIO shieldsIO

WideImg

Master en Programación de Aplicaciones con JavaScript y Node.js

JS, Node.js, Frontend, Express, Patrones, IoT, HTML5_APIs, Asincronía, Websockets, ECMA6, ECMA7

Clase 107

Empieza Nodejs Avanzado!

Temario

  • Desarrollo de librerías isomórficas
  • Integración de clientes y plataformas de mensajería (Slack, Hangouts, Telegram...)
  • Trabajando con otros lenguajes de programación desde Node.js (Python, Bash, etc..)
  • Creación de aplicaciones CLI
    • Reusabilidad
    • Gestión de la información
    • Soporte multiplataforma
  • Gestión de tareas periódicas
    • Trabajando con fechas CRON
    • Scheduled
  • Scraping
    • Librerías propias de Node.js como «X-Ray», «Cheerio», etc...
    • Integración de librerías de otros lenguajes como «Beautiful Soup» de Python
  • Internet of Things (IoT) y Robótica
    • Trabajando con comunicación serial (USB, Bluetooth)
    • Arduino y su ecosistema
    • Sensores (potenciómetros, lumínicos, sonoros, proximidad…)
    • Actuadores (motores, leds, servos…)
    • Trabajando desde Node.js con librerías como Johnny-Five, Cyclon.js…
    • Otras formas de trabajar con la electrónica
  • Creación de Apps híbridas con PhoneGap y Apache Cordova
  • Creación de aplicaciones HTML5 de escritorio (Electron, Photon, etc...)

Nuevo Proyecto personal (Proyecto fin de master)

reto

Imprescindibles para este proyecto:

  • Web Backend (Rutas Backend, Nodejs y/o Cloud functions)
  • Web Frontend (Ajax, HTML5 Apis, rutas front, etc...)
  • Scraping
  • Uso de patrones

No críticos pero puntuables para este proyecto:

  • Versión de escritorio
  • Version App Hibrida
  • Integración con IOT
  • Version CLI

Extras:

  • Testing
  • JSDocs
  • Gestión del proyecto desde el inicio en un repositorio propio en Github
  • Firebase u otra base de datos
  • WebSockets
  • Sin frameworks ni librerias externas en la medida de lo posible

Fecha de entrega:

  • Septiembre 2018

Claves Avanzadas de Nodejs

Process

  • Códigos de salida en Node.js

    • 1 - Uncaught Fatal Exception - No ha podido ser capturada
    • 2 - Unused (reserved by Bash for builtin misuse)
    • 3 - *Internal JavaScript Parse Error *
    • 4 - *Internal JavaScript Evaluation Failure *
    • 5 - Fatal Error - There was a fatal unrecoverable error in V8.
    • 6 - Non-function Internal Exception Handler
    • 7 - Internal Exception Handler Run-Time Failure
    • 8 - Unused
    • 9 - Invalid Argument
    • 10 - *Internal JavaScript Run-Time Failure *
    • 12 - Invalid Debug Argument
    • 128 - Signal Exits - El sistema operativo acaba con Node.
  • process.argv:

  console.log(process.argv)
  /*
  1. ubicacion de node (bin)
  2. ubicación del script (location)
  3. [Otros parametros]
  */
  
  • Detalles del sistema
	process.argv[2] = process.argv[2] || "Node.js funcionando desde C9.io";

	console.log("===================================");
	console.log("Id: " + process.pid);
	console.log("Título: " + process.title);
	console.log("Ruta: " + process.execPath);
	console.log("Directorio Actual: " + process.cwd());
	console.log("Node Versión: " + process.version);
	console.log("Plataforma: " + process.platform);
	console.log("Arquitectura: " + process.arch);
	console.log("Tiempo activo: " + process.uptime());
	console.log("Versiones Dependencias: ");
	process.argv.forEach((val, index, array) => {
	      console.log(`${index}: ${val}`);
	});
	console.log("===================================");
  • console.log("Hola"):
  var mensaje = "Hola"
  process.stdout.write(mensaje + '\n')

Captura de errores inesperados

  process.on('uncaughtException', function (err) {
  	console.error(error.stack);
  });
  • Es interesante almacenar estos errores o enviarlos por email.
  • Estos errores se escapan del sistema convencional de errores.

Ejecucción de tareas antes de la finalización del proceso

  process.on('exit', function (err) {
  	// Limpiar cache.. y cosas similares...
  });

Captura de señales en entronos UNIX

// SIGINT -> Control-C

process.stdin.resume(); // Evitamos que se cierre Node.js

process.on('SIGINT', function() {
  console.log('Llegó SIGINT. Control-C... Saliendo...');
  process.exit(0);
});

Creando ejecutables

  • Solo para entornos UNIX

  • Necesitamos shebang

	#!/usr/bin/env node
	console.log('Soy un script!');
  • Necesitamos hacer el script ejecutable
chmod +x mi_escript_archivo.js
  • Ejecutando el script
./mi_escript_archivo.js <parámetro>

Ejemplo: hola

#!/usr/bin/env node
console.log("hola");
process.exit(1);

Arrancar Scripts al incio del sistema

Child Process

  • Ideal para tareas pesadas, inestables o muy lentas

  • Nos permite usar comandos del sistema.

  • Podemos lanzar aplicaciones basadas en otros lenguajes o sistemas.

  • Creando hijos y usando spawn

  • spawn devuelve un stream

var spawn = require('child_process').spawn;
var ping = spawn('ping', ['fictizia.com']);

ping.stdout.setEncoding('utf8');
ping.stdout.on('data', function (data) {
  console.log(data);
});
  • Creando hijos y usando exec
  • exec retorna un buffer
  var exec = require('child_process').exec

  // cat solo funciona en UNIX
  exec('cat texto.txt', function (err, stdout, stderr) {
    if(!err){  
        console.log('El contenido de nuestro archivo', stdout)
    } else {
        console.log('Error: '+err)
    }
  })
  • Manejando hijos:
  var spawn = require('child_process').spawn

  if(process.argv[2] === 'hijo'){
    console.log('Estoy dentro del proceso hijo');
  } else {
    var hijo = spawn(process.execPath, [__filename, 'hijo'])
    hijo.stdout.pipe(process.stdout)
  }
  • Manejando hijos (con heréncia):
  var spawn = require('child_process').spawn

  if(process.argv[2] === 'hijo'){
    console.log('Estoy dentro del proceso hijo');
  } else {
    var hijo = spawn(process.execPath, [__filename, 'hijo'], {
      stdio: 'inherit'
    })
  }
  • Manejando hijos (contexto común):
  var spawn = require('child_process').spawn;

  var contador = 0;
  contador += 1;

  if(process.argv[2] === 'hijo'){
    console.log('hijo', contador);
    contador++;
    console.log('pero.. además el hijo.. suma otra! y son', contador);
  } else {
    var hijo = spawn(process.execPath, [__filename, 'hijo'], {
      stdio: 'inherit'
    });
    console.log('padre', contador);
  }

Cluster

  • Sin usar Cluster
    var http = require('http');
    var url = require('url');

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

    server.on('request', function (req, res) {
        var pathname = url.parse(req.url).pathname;
        if (pathname === '/matame') {
            res.writeHead(200, {
                'Content-Type': 'text/plain'
            });
            res.end('Has matado el monohilo. PID: ' + process.pid);
            process.exit(0);
        } else {
            res.writeHead(200, {
                'Content-Type': 'text/plain'
            });
            res.end('Hola desde el monohilo. PID: ' + process.pid);
        }
    });

    console.log('Servidor escuchando por el puerto ' +process.env.PORT);
  • Usando Cluster.
    • El proceso hijo que cae se vuelve a levantar.
    • El proceso padre se mantiene "separado"
	var cluster = require('cluster'),
	    http = require('http'),
	    url = require('url'),
	    cpus = require('os').cpus().length; // nproc
	
	if (cluster.isMaster) {
	    console.log('Proceso maestro con PID:', process.pid);
	
	    for (var i = 0; i < cpus; i++) {
	        cluster.fork();
	    }
	
	    cluster.on('exit', function(worker) {
	        console.log('hijo con PID ' + worker.process.pid + ' muerto');
	        cluster.fork();
	    });
	
	} else {
	    console.log('Arrancado hijo con PID:', process.pid);
	
	    var server = http.createServer().listen(process.env.PORT);
	
	    server.on('request', function (req, res) {
	        var pathname = url.parse(req.url).pathname;
	        if (pathname === '/matame') {
	            res.writeHead(200, {
	                'Content-Type': 'text/plain'
	            });
	            res.end('Has matado al proceso hijo ' + process.pid);
	            process.exit(0);
	        } else {
	            res.writeHead(200, {
	                'Content-Type': 'text/plain'
	            });
	            res.end('Hola desde ' + process.pid);
	        }
	    });
	}

Buffer

  • Nos ofrece la posibilidad de alamacenar datos sin procesar
  • Una vez iniciados no puede modificarse el tamaño
  • Tamaño máximo 1GB
const buf1 = new Buffer(10); // buf1.length = 10
const buf2 = new Buffer([1,2,3]); // [01, 02, 03]
const buf3 = new Buffer('prueba'); // ASCII [74, 65, 73, 74]
const buf4 = new Buffer('ñam ñam', 'utf8'); // UTF8 [74, c3, a9, 73, 74]
  
console.log("===================================");
console.log("buf1: " + buf1.length);
console.log("buf2: " + buf2[0]);
console.log("buf3: " + buf3);
console.log("buf3 (hex): " + buf3.toString('hex'));
console.log("buf3 (base64): " + buf3.toString('base64'));
console.log("buf4: " + buf4);
console.log("buf4 (hex): " + buf4.toString('hex'));
console.log("buf4 (base64): " + buf4.toString('base64'));
console.log("===================================");

Stream

  • Gestionamos el flujo de datos

  • Muy usados por librerías y modulos

  • Capa de abstracción para operaciones con datos

  • Lógica de tuberias (cadena de procesos)

  • Gestiona el buffer por si mismo

  • Tipos:

    • Redeable Lectura
      • Eventos (data, error, end)
    • Writable Escritura
      • Eventos (drain, error, finish)
    • Duplex Ambos
    • Transform Transformación de datos
    • PassThrough No manipula, solo manipula
  • Función .pipe()

    • Simple: origen.pipe(destino);
    • Concatenando: origen.pipe(destino).pipe(otroDestino);
  • Ejemplo: Stream multimédia

	var http = require('http'),
	    fs = require('fs');
	
	http.createServer(function(req, res) {
	  var cancion = 'cancion.mp3';
	  var stat = fs.statSync(cancion);
	
	  res.writeHead(200, {
	    'Content-Type': 'audio/mpeg',
	    'Content-Length': stat.size
	  });
	
	  var readableStream = fs.createReadStream(cancion);
	  readableStream.pipe(res);
	
	}).listen(process.env.PORT);
  • Stream y ficheros
    var fs = require('fs');
    
    var lectura = fs.createReadStream('archivo1.txt');
    var escritura = fs.createWriteStream('otroArchivo.txt');
    
    lectura.pipe(escritura);

Variables del Entorno

  • Conocer todas las variables disponibles en el entorno

    • Windows:
     	env
    
    • UNIX:
     	SET
    
  • Guardar nuevas variables en el entorno

    • Windows:
     	SET ALGO='mi secreto'
    
    • UNIX:
     	export ALGO='mi secreto'
    
  • Recuperar las variables con Node.js

     	var datoRecuperado = process.env.ALGO;
     	console.log(process.env.ALGO);
  • Creando variables del entorno limitadas a Node.js y temporales (SOLO UNIX)

    • Arrancando...
     	NODE_ENV=production node app.js
    
    • Usando datos...
     	if(process.env.NODE_ENV === "production"){
     		console.log("Entramos en modo producción");
     	} else if (process.env.NODE_ENV === "development"){
     		console.log("Entramos en modo desarrollo");
     	} else {
     		console.log("Entramos en modo desconocido. ¡Revisa las variables del entorno!");
     	}
  • Alternativas

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.
    • var exports = module.exports = {};
  • Es importante controlar la reasignación de module.exports

Modularización: Usando exports

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

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

function privada (){
	return datoPrivado;
}

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

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

Modularización: Usando module.exports

  • Exportar los datos:
// archivo -> config.js
var config = {
  token: "<--- MiSecreto--->",
};

module.exports = config;
  • Importar los datos:
var config = require('./config');

console.log(config.token);