JS, Node.js, Frontend, Backend, Firebase, Express, Patrones, HTML5_APIs, Asincronía, Websockets, Testing
Recursos
- i0natan/nodebestpractices The largest Node.JS best practices list (November 2018)
- Node.js Best Practices - How to Become a Better Developer in 2017
- 25 development practices our NodeJS developers follow
- 19 ways to become a better Node.js developer in 2019
Tipos de error
Explicit exceptions
, cuando usamosthrow
Implicit exceptions
, como cuando no definimos una variable (ReferenceError)- El evento
error
deEventEmitter
que puede terminar con el proceso, aplicable nativamente a Streams, Servers, Requests/Responses, Child processes, etc... - El argumento
error
en un callback - El
reject
de unaPromise
Claves
- Utiliza siempre try/catch con Async/Await
- Utiliza siempre catch para gestionar problemas en flujos de promesas
- Utiliza
Error-First Callbacks
para gestionar errores con callbacks - Usa un logger para tener una trazabilidad real de los errores
- Entender las diferencias entre
Operational errors
yprogrammer errors
.
Recursos
- The Node.js Way - Understanding Error-First Callbacks
- Node.js Best Practices | Error Handling Practices
- Building Robust Node Applications: Error Handling
- Joyent | Production Practices
- How to prevent your Node.js process from crashing
node --inspect <your_file>.js # Normal
node --inspect-brk <your_file>.js # Con breakpoints
# Abrir about:inspect en Chrome
Librerías de Logging
- pino Extremely fast logger inspired by Bunyan.
- winston Multi-transport async logging library.
- console-log-level The most simple logger imaginable with support for log levels and custom prefixes.
- storyboard End-to-end, hierarchical, real-time, colorful logs and stories.
- signale Hackable console logger with beautiful output.
Librerías de Debugging / Profiling
- ndb Improved debugging experience, enabled by Chrome DevTools.
- ironNode Node.js debugger supporting ES2015 out of the box.
- node-inspector Debugger based on Blink Developer Tools.
- debug Tiny debugging utility.
- why-is-node-running Node.js is running but you don't know why?
- njsTrace Instrument and trace your code, see all function calls, arguments, return values, as well as the time spent in each function.
- vstream Instrumentable streams mix-ins to inspect a pipeline of streams.
- stackman Enhance an error stacktrace with code excerpts and other goodies.
- locus Starts a REPL at runtime that has access to all variables.
- 0x Flamegraph profiling.
- ctrace Well-formatted and improved trace system calls and signals.
- leakage Write memory leak tests.
- llnode Post-mortem analysis tool which allows you to inspect objects and get insights from a crashed Node.js process.
Recursos
- Debugging Node.js with Google Chrome
- Node | Debugging Guide
- Node Summit | Debugging in 2017 with Node.js
- Step-by-step guide to debugging NodeJS applications
- How to Debug Node.js with the Best Tools Available
- Debugging Node.js con Node Inspector
// Esto es univeral
function saludar () {
console.log('Hola Mundo!');
}
// Esto NO es universal
document.body.innerText = "Hola Mundo!"
Claves
- El código universal debe poder funcionar en cliente y servidor
- Cosas problematicas en cliente
- DOM (
document
,window
) - HTML5 Apis (localstorage, geolocation, etc..)
- Ajax (
XMLHttpRequest
,fecth
) - Librerias de terceros (
Jquery
, etc...)
- DOM (
- Cosas problematicas en Node
- Librerías de sistema (
http
,process
, etc...) - Librerias de terceros (
Express
)
- Librerías de sistema (
Patrones
Backbone...
(function(){
// The top-level namespace. All public Backbone classes and modules will
// be attached to this. Exported for both CommonJS and the browser.
var Backbone;
if (typeof exports !== 'undefined') {
Backbone = exports;
} else {
Backbone = this.Backbone = {};
}
// ...
})();
Axel Rauschmayer...
"use strict";
(function(exports) {
// Código de Nodejs
}(typeof exports === "undefined" ? (this.moduleName = {}) : exports));
UMD (Universal Module Definition) patterns for JavaScript modules that work everywhere.
- Módulo compatible con Node y AMD
- Módulo compatible con CommonJS y AMD
- Plugin de Jquery que funciona con CommonJS, AMD o globals (navegador)
- Módulo compatible con CommonJS, AMD o globals (navegador)
- Módulo compatible con AMD y globals (navegador)
Build tools
- docpad-plugin-umd Wrap specified JavaScript documents in the Universal Module Definition (UMD) allowing them to run in AMD, Require.js, CommonJS/Node.js and Vanilla environments automatically
- grunt-umd Surrounds code with the universal module definition (MIT)
- gulp-umd Gulp plugin for build JavaScript files as Universal Module Definition, aka UMD
- grunt-urequire Grunt wrapper for uRequire
- generator-umd An Yeoman Generator to create a basic UMD structure
Librerias isomórficas como ejemplo
Recursos
- How to write and unit-test universal JavaScript modules (browser, Node.js)
- Isomorphic (Universal) JavaScript
- Universal JS module loader
- A 10 minute primer to JavaScript modules, module formats, module loaders and module bundlers
- How to write and build JS libraries in 2018
- Isomorphic ES Modules
- Writing JS libraries less than 1TB size
- 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("Argumentos: ");
process.argv.forEach((val, index, array) => {
console.log(`${index}: ${val}`);
});
console.log("===================================");
console.log("Hola"):
process.stdout.write("Hola")
Captura de errores inesperados
- Es interesante almacenar estos errores o enviarlos por email.
- Estos errores se escapan del sistema convencional de errores.
process.on('uncaughtException', err => {
console.error(error.stack);
});
Ejecucción de tareas antes de la finalización del proceso
process.on('exit', 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', () => {
console.log('Llegó SIGINT. Control-C... Saliendo...');
process.exit(0);
});
Las variables de entorno son una lista de ajustes que guardan varios estados de una sesión. Cuando se inicia una sesión ya sea en el entrono gráfico o en una terminal, se leen las variables de entorno.
Conocer todas las variables disponibles en el entorno
# Windows
env
# UNIX
SET
Guardar nuevas variables en el entorno de forma permanente
# Windows
SET ALGO='mi secreto'
# UNIX
export ALGO='mi secreto'
Recuperar las variables con Node.js
export ALGO='mi secreto'node <fichero>.js
const datoRecuperado = process.env.ALGO;
console.log(datoRecuperado); // mi secreto
Creando variables del entorno limitadas a Node.js y temporales (SOLO UNIX)
NODE_ENV=production node app.js
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!");
}
Librerías
- dotenv Loads environment variables from .env for nodejs projects.
- cross-env Set environment variables cross-platform.
Recursos
- Working with Environment Variables in Node.js
- process.env: What it is and why/when/how to use it effectively
- Environment Variables in Node.js
- Here’s how you can actually use Node environment variables
- Using dotenv package to create environment variables
- Configuration settings in Node with dotenv
- Managing Configurations in Node.JS apps with dotenv and convict
En informática, un ejecutable o archivo ejecutable, es tradicionalmente un archivo binario, cuyo contenido se interpreta por el ordenador como un programa.
Generalmente, contiene instrucciones en código máquina de un procesador en concreto, pero también puede contener bytecode que requiera un intérprete para ejecutarlo. Además, suele contener llamadas a funciones específicas de un sistema operativo (llamadas al sistema).
Dependiendo del tipo de instrucciones de que se traten, hablaremos de ejecutables portables (se pueden ejecutar en varias plataformas) y no portables (destinado a una plataforma concreta). Wikipedia
Claves
- Solo para entornos UNIX
- Necesitamos shebang
- Necesitamos hacer el script ejecutable
Añadir el shebang
#!/usr/bin/env node
console.log('Soy un script!');
Hacer el script ejecutable
chmod +x mi_escript_archivo.js
Ejecutando el script
./mi_escript_archivo.js <parámetro>
Ejemplo
#!/usr/bin/env node
console.log("hola");
process.exit(1); //Opcional
Arrancar Scripts al incio del sistema con Librerias
- node-upstarter Easily create upstart services for your node apps
- diable 😈 Daemonize the things out.
- daemonize-process Daemonize the current Node.js process
- daemonix A utility for creating daemons out of NodeJS applications.
Los buffer son conjuntos de datos en crudo, datos binarios, que podemos tratar en NodeJS para realizar diversos tipos de acciones. Los implementa Node a través de una clase específica llamada Buffer, que era necesaria porque Javascript tradicionalmente no era capaz de trabajar con tipos de datos binarios.
Los buffer son similares a arrays de enteros, en los que tenemos bytes que corresponden con datos en crudo de posiciones de memoria fuera de la pila de V8. Aunque desde Javascript ES6 con los TypedArray ya es posible trabajar con un buffer de datos binarios, NodeJS mantiene su clase Buffer que realiza un tratamiento de la información más optimizado para sus casos de uso. Desarrollo Web
Claves
- Nos ofrece la posibilidad de almacenar datos sin procesar
- Una vez iniciados no puede modificarse el tamaño
- Permite realizar transformaciones y operaciones en crudo (binarios)
- Los buffers están muy presentes en Nodejs, pero son de muy bajo nivel
- El tamaño máximo es de 1GB
- La representacion por defecto es en bytes
- 1 byte es igual a 8 bits
- El byte almacena valores de 0 a 255, en su versión hezadecimal de 00 a FF
new Buffer()
se deprecó a favor deBuffer.from()
yBuffer.alloc()
Ejemplos
const buf1 = Buffer.alloc(5);
console.log(buf1); // <Buffer 00 00 00 00 00>
const buf2 = Buffer.from('Fictizia');
console.log(buf2) // <Buffer 46 69 63 74 69 7a 69 61>
console.log("buf2 valor real:", buf2.toString()); // Fictizia
Recursos
- Do you want a better understanding of Buffer in Node.js? Check this out
- Buffer en NodeJS
- Nodejs Docs | Buffer
- An Overview of Buffers in Node.js
Los streams son collecciones/flujos de datos — algo parecido a los Arrays
o los Strings
.
La diferencia es que los datos del stream pueden no estar siempre disponibles, y pueden "contener" más datos que el límite de la memoria.
Una de las propiedades más importantes es el encadenamiento, podemos modificar los datos iniciales como si fuésemos encadenando comando de bash:
$ echo "Hola" | grep "o"
Claves
- 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
Ejemplos de streams en Node.js:
Readable
(lectura): Es una abstracción de un conjunto de datos de entrada, por ejemplofs.createReadStream()
.Writable
(escritura): Es una abstracción del destino en el que será escrito, por ejemplofs.createWriteStream()
.Duplex
(lectura y escritura): Por ejemplo un socket TCP.Transform
: Un stream que a parte de leer y escribir va transformando los datos a medida que van llegando, por ejemplozlib.createGzip()
.
Todos los streams son instancias de
EventEmitter
, emiten eventos a medida que leen y escriben datos. Sin embargo, podemos consumir y encadenar streams de una manera sencilla utilizando la funciónpipe()
.
readableSrc
.pipe(transformStream1)
.pipe(transformStream2)
.pipe(finalWrtitableDest)
La función pipe()
devuelve la salida del stream anterior:
a.pipe(b).pipe(c).pipe(d)
// Es equivalente a:
a.pipe(b);
b.pipe(c);
c.pipe(d);
// En Linux, es equivalente a:
$ a | b | c | d
Los eventos más importantes de un stream de lectura son:
data
: Cada vez que se procesa un trozo del dato.end
: Cuando ya se han emitido la totalidad de los datos.
Los eventos más importantes de un stream de escritura son:
drain
: Cuando el stream está disponible para recibir más datos.finish
: Cuando ya se han liberado todos los datos del stream (se vacía).
// readable.pipe(writable)
readable.on('data', (chunk) => {
writable.write(chunk);
});
readable.on('end', () => {
writable.end();
});
Implementando un stream de lectura y uno de escritura
const { Readable, Writable } = require('stream');
const inStream = new Readable({
read(size) {
this.push(String.fromCharCode(this.currentCharCode++));
if (this.currentCharCode > 90) {
this.push(null);
}
}
});
const outStream = new Writable({
write(chunk, encoding, callback) {
console.log(chunk.toString())
callback();
}
});
inStream.currentCharCode = 65;
inStream.pipe(outStream);
Streams multimédia
const http = require('http'),
fs = require('fs');
http.createServer((req, res) => {
const cancion = 'cancion.mp3';
const stat = fs.statSync(cancion);
res.writeHead(200, {
'Content-Type': 'audio/mpeg',
'Content-Length': stat.size
});
const readableStream = fs.createReadStream(cancion);
readableStream.pipe(res);
}).listen(process.env.PORT);
Streams y ficheros
const fs = require('fs');
const lectura = fs.createReadStream('archivo1.txt');
const escritura = fs.createWriteStream('otroArchivo.txt');
lectura.pipe(escritura);
Recursos
- Node.js Streams: Everything you need to know
- A Brief History of Node Streams
- NODE.JS STREAMS
- El manejo de streams en NodeJS
- Events and Streams in Node.js
- awesome-nodejs-streams
- Stream and Buffer Concepts in Node.js
- Node.js streams cheatsheet
Claves
- Ideal para tareas pesadas, inestables o muy lentas
- Nos permite usar comandos del sistema.
- Podemos lanzar aplicaciones basadas en otros lenguajes o sistemas.
stdout
suele referirse a la salida de datos del proceso hijo (sin errores)stderr
es la referencia a la salida de datos con errores- En ocasiones lanzamos procesos que tiene
stderr
ystdout
cambiado
Métodos esenciales
child_process.exec()
genera un shell y ejecuta un comando dentro de ese shell, pasando elstdout
y elstderr
al callback cuando se completa. Internamente utilizabuffer
Docchild_process.execSync()
una versión síncrona y bloqueante dechild_process.exec()
Docchild_process.execFile()
similar achild_process.exec()
, excepto que genera el comando directamente sin generar primero un shell de forma predeterminada Docchild_process.execFileSync()
una versión síncrona y bloqueante dechild_process.execFile()
Docchild_process.spawn()
genera un shell y ejecuta un comando devolviendo unstream
y que debemos gestionar por eventos Docchild_process.spawnSync()
una versión síncrona y bloqueante dechild_process.spawn()
Docchild_process.fork()
Es similar achild_process.spawn()
solo que nos permite enviar mensajes al proceso hijo Doc
Relación
Librerías
- execa A better
child_process
- opn A better node-open. Opens stuff like websites, files, executables. Cross-platform.
- node-worker-farm Distribute processing tasks to child processes with an über-simple API and baked-in durability & custom concurrency options.
- spawnd Spawn a process inter-dependent with parent process.
Recursos
- Node.js Child Processes: Everything you need to know
- Understanding execFile, spawn, exec, and fork in Node.js
- Node.js: managing child processes
- Getting to know Node’s child_process module
- Nodejs Doc | exec
- Nodejs Doc | execFile
- Nodejs Doc | fork
- Nodejs Doc | execSync
- Nodejs Doc | execFileSync
- Nodejs Doc | spawnSync
- Nodejs Doc | spawn
Proceso hijo que tiene un fin
cat texto.txt
const {exec} = require('child_process');
// cat solo funciona en UNIX
exec('cat texto.txt', (err, stdout, stderr) => {
if(!err){
console.log('El contenido de nuestro archivo', stdout)
} else {
console.log('Error: '+err)
}
})
Proceso hijo que nunca termina
ping fictizia.com
const {spawn} = require('child_process'),
ping = spawn('ping', ['fictizia.com']);
ping.stdout.setEncoding('utf8');
ping.stdout.on('data', console.log);
Claves
- Las instancias de
ChildProcess
conEventEmitters
que representan a los procesos hijos. - Se generan con los métodos
child_process.spawn()
,child_process.exec()
,child_process.execFile()
ychild_process.fork()
- Podemos suscribirnos a eventos:
close
se emite cuando el proceso hijo ha terminado por si mismo.disconnect
se emite cuando el padre o el hijo ejecutan el métododisconnect()
.error
se emite cuando se produce algún error (no puede lanzar, no puede cerrar el proceso, etc...).exit
se emite cuando el proceso hijo ha salido por si mismo.message
se emite cuando un proceso hijo utiliza el métodosend()
.
- Podemos suscribirnos al evento
data
de todos los stdio (standard input-output):child.stdin
inputchild.stdout
outputchild.stderr
error
Ejemplo
ls -lh /usr
const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']);
ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
ls.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
});
ls.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});
Claves
- Es una instancia de
ChildProcess
que contiene una API basada eneventEmitter
- No genera una terminal en si misma, por lo que es más eficiente que
process.exec()
- Es la mejor opción para gestionar procesos muy largos y bloqueantes o que contengan una salida de datos muy grande
- Muy útil cuando tenemos proceso que no terminan como escucha de puertos, ping, etc...
Estructura: child_process.spawn(command[, args][, options])
command
(string) Comando por ejecutarargs
(string[]) Lista de argumentosoptions
Podemos pasar un objeto de configuración que cuenta con multiples opcionescwd
(string) Definir el directorio de trabajo.env
(Object) Variables de entorno (clave-valor).argv0
(string) Permite especificar elargv[0]
que se envia al proceso hijo.stdio
(Array|string) Opciones para la comunicacion padre-hijo con el streamoptions.stdio
.detached
(boolean) Permite que el hijo se ejecute de forma independiente al padre. Cambia según la plataformaoptions.detached
.uid
(number) Define el UID del proceso. Doc.gid
(number) Define el GID del proceso. Doc.shell
(boolean|string) si es true, ejecuta el comando dentro de una shell./bin/sh
(UNIX) oprocess.env.ComSpec
(Windows). Se puede definir otra shell distintawindowsVerbatimArguments
(boolean) No se citan ni se escapan argumentos en Windows. No aplicable a platformas. Default: false.windowsHide
(boolean) Oculta la ventana de subproceso que se genera en Windows normalmente. No aplicable a platformas. Default: false.
- Retorna (
ChildProcess
)
Estructura: child_process.spawnSync(command[, args][, options])
command
(string) Comando por ejecutarargs
(string[]) Lista de argumentosoptions
Podemos pasar un objeto de configuración que cuenta con multiples opcionescwd
(string) Definir el directorio de trabajo.env
(Object) Variables de entorno (clave-valor).argv0
(string) Permite especificar elargv[0]
que se envia al proceso hijo.stdio
(Array|string) Opciones para la comunicacion padre-hijo con el streamoptions.stdio
.uid
(number) Define el UID del proceso. Doc.gid
(number) Define el GID del proceso. Doc.shell
(boolean|string) si es true, ejecuta el comando dentro de una shell./bin/sh
(UNIX) oprocess.env.ComSpec
(Windows). Se puede definir otra shell distintawindowsVerbatimArguments
(boolean) No se citan ni se escapan argumentos en Windows. No aplicable a platformas. Default: false.windowsHide
(boolean) Oculta la ventana de subproceso que se genera en Windows normalmente. No aplicable a platformas. Default: false.input
(string|Buffer|TypedArray|DataView) valor que sobreescribestdio[0]
process.env.ComSpec
(string) Solo para Windows. Permite especificar otra shell.encoding
(string) codificación de entrada y salida. Default:buffer
maxBuffer
(number) Define la longitud máxima en datos permitido parastdout
ostderr
. Default: 200 * 1024.killSignal
(string|integer) El valor d ela señal que se aplique cuando el proceso hijo muera.timeout
(number) Tiempo en ms que el proceso correrá como máximo. Default:undefined
- Returns (Object)
pid
(number) Pid del proceso hijo.output
(Array) Los distintos outputs del proceso hijo.stdout
(Buffer|string) contenido deloutput[1]
.stderr
(Buffer|string) contenido deloutput[2]
.status
(number) El código de salida del proceso hijo.signal
(string) La señal usada para matar al proceso hijo.error
(Error) El objeto de error si algo fue mal o se alcanzo el fin del tiempo de ejecución.
Usando child_process.spawn()
ping fictizia
const {spawn} = require('child_process'),
ping = spawn('ping', ['fictizia.com']);
ping.stdout.setEncoding('utf8');
ping.stdout.on('data', console.log);
Usando child_process.spawnSync()
ls -lh /usr
grep local
const { spawnSync } = require('child_process');
console.log('[Empieza] ls');
const ls = spawnSync('ls', ['-lh', '/usr']);
console.log('[Termina] ls');
console.log('[Empieza] grep');
const grep = spawnSync('grep', ['local'], {
input: ls.stdout
});
console.log('[Termina] grep');
process.on('exit', function() {
console.log(`[GREP] ${grep.stdout.toString()}`);
});
Gestión de errores
bad_command
const { spawn } = require('child_process');
const subprocess = spawn('bad_command');
subprocess.on('error', (err) => {
console.log('Failed to start subprocess.');
});
Claves
- Es menos eficiente que
process.spawn
ya que generamos una subshell nueva - Es una buena opción si el buffer de datos que generamos es pequeño y tulizamos sintaxis de shell
- Es peligroso porque puede generar inyecciones de comandos
Estructura: child_process.exec(command[, options][, callback])
command
(string) Comando por ejecutaroptions
Podemos pasar un objeto de configuración que cuenta con multiples opcionescwd
(string) Definir el directorio de trabajo.env
(Object) Variables de entorno (clave-valor).uid
(number) Define el UID del proceso. Doc.gid
(number) Define el GID del proceso. Doc.shell
(boolean|string) si es true, ejecuta el comando dentro de una shell./bin/sh
(UNIX) oprocess.env.ComSpec
(Windows). Se puede definir otra shell distintaprocess.env.ComSpec
(string) Solo para Windows. Permite especificar otra shell.encoding
(string) codificación de entrada y salida. Default:buffer
maxBuffer
(number) Define la longitud máxima en datos permitido parastdout
ostderr
. Default: 200 * 1024.killSignal
(string|integer) El valor d ela señal que se aplique cuando el proceso hijo muera.timeout
(number) Tiempo en ms que el proceso correrá como máximo. Default:undefined
windowsHide
(boolean) Oculta la ventana de subproceso que se genera en Windows normalmente. No aplicable a platformas. Default: false.
callback
(Function) callback que se ejecuta cuando el proceso terminaerror
(Error) En caso de contener algun errorstdout
(string|Buffer) output de salidastderr
(string|Buffer) output de error
- Retorna (
ChildProcess
)
Estructura: child_process.execSync(command[, options])
command
(string) Comando por ejecutaroptions
Podemos pasar un objeto de configuración que cuenta con multiples opcionescwd
(string) Definir el directorio de trabajo.input
(string|Buffer|TypedArray|DataView) valor que sobreescribestdio[0]
stdio
(Array|string) Opciones para la comunicacion padre-hijo con el streamoptions.stdio
.shell
(boolean|string) si es true, ejecuta el comando dentro de una shell./bin/sh
(UNIX) oprocess.env.ComSpec
(Windows). Se puede definir otra shell distintaenv
(Object) Variables de entorno (clave-valor).process.env.ComSpec
(string) Solo para Windows. Permite especificar otra shell.uid
(number) Define el UID del proceso. Doc.gid
(number) Define el GID del proceso. Doc.windowsHide
(boolean) Oculta la ventana de subproceso que se genera en Windows normalmente. No aplicable a platformas. Default: false.encoding
(string) codificación de entrada y salida. Default:buffer
maxBuffer
(number) Define la longitud máxima en datos permitido parastdout
ostderr
. Default: 200 * 1024.killSignal
(string|integer) El valor d ela señal que se aplique cuando el proceso hijo muera.timeout
(number) Tiempo en ms que el proceso correrá como máximo. Default:undefined
- Returns (Buffer|string) contenido del
stdout
Usando child_process.exec()
cat texto.txt
const {exec} = require('child_process');
// cat solo funciona en UNIX
exec('cat texto.txt', (err, stdout, stderr) => {
if(!err){
console.log('El contenido de nuestro archivo', stdout)
} else {
console.log('Error: '+err)
}
})
Usando child_process.execSync()
cat texto.txt
const {execSync} = require('child_process');
const cmd = 'cat texto.txt';
// cat solo funciona en UNIX
console.log(`contenido: ${execSync(cmd)}`)
//contenido: holaaaa
Claves
- Es similar a
child_process.exec()
pero no genera una shell durante la ejecucción. Es la mejor opción cuando queremos ejecutar ficheros.bat
o.cmd
- Es ideal para lanzar una aplicación y recoger el output
Estructura: child_process.execFile(file[, args][, options][, callback])
file
(string) nombre o ruta del ficheroargs
(string[]) Lista de argumentosoptions
Podemos pasar un objeto de configuración que cuenta con multiples opcionescwd
(string) Definir el directorio de trabajo.env
(Object) Variables de entorno (clave-valor).encoding
(string) codificación de entrada y salida. Default:buffer
timeout
(number) Tiempo en ms que el proceso correrá como máximo. Default:undefined
maxBuffer
(number) Define la longitud máxima en datos permitido parastdout
ostderr
. Default: 200 * 1024.killSignal
(string|integer) El valor d ela señal que se aplique cuando el proceso hijo muera.windowsVerbatimArguments
(boolean) No se citan ni se escapan argumentos en Windows. No aplicable a platformas. Default: false.uid
(number) Define el UID del proceso. Doc.gid
(number) Define el GID del proceso. Doc.shell
(boolean|string) si es true, ejecuta el comando dentro de una shell./bin/sh
(UNIX) oprocess.env.ComSpec
(Windows). Se puede definir otra shell distintawindowsHide
(boolean) Oculta la ventana de subproceso que se genera en Windows normalmente. No aplicable a platformas. Default: false.
callback
(Function) callback que se ejecuta cuando el proceso terminaerror
(Error) En caso de contener algun errorstdout
(string|Buffer) output de salidastderr
(string|Buffer) output de error
- Retorna (
ChildProcess
)
Estructura: child_process.execFileSync(file[, args][, options])
file
(string) nombre o ruta del ficheroargs
(string[]) Lista de argumentosoptions
Podemos pasar un objeto de configuración que cuenta con multiples opcionescwd
(string) Definir el directorio de trabajo.input
(string|Buffer|TypedArray|DataView) valor que sobreescribestdio[0]
stdio
(Array|string) Opciones para la comunicacion padre-hijo con el streamoptions.stdio
.shell
(boolean|string) si es true, ejecuta el comando dentro de una shell./bin/sh
(UNIX) oprocess.env.ComSpec
(Windows). Se puede definir otra shell distintaenv
(Object) Variables de entorno (clave-valor).uid
(number) Define el UID del proceso. Doc.gid
(number) Define el GID del proceso. Doc.windowsHide
(boolean) Oculta la ventana de subproceso que se genera en Windows normalmente. No aplicable a platformas. Default: false.encoding
(string) codificación de entrada y salida. Default:buffer
maxBuffer
(number) Define la longitud máxima en datos permitido parastdout
ostderr
. Default: 200 * 1024.killSignal
(string|integer) El valor d ela señal que se aplique cuando el proceso hijo muera.timeout
(number) Tiempo en ms que el proceso correrá como máximo. Default:undefined
- Returns (Buffer|string) contenido del
stdout
Usando child_process.execFile()
node --version
const { execFile } = require('child_process');
execFile('node', ['--version'], (error, stdout, stderr) => {
if (error) {
throw error;
}
console.log(stdout);
});
Usando child_process.execFileSync()
node --version
const { execFileSync } = require('child_process');
const child = execFileSync('node', ['--version'])
console.log(`stdout: ${child}`);
Claves
- Es una variantes de
child_process.spawn()
- Nos permite enviar información al proceso hijo usando métodos especiales
Estructura: child_process.fork(modulePath[, args][, options])
modulePath
(string) Módulo por ejecutarargs
(string[]) Lista de argumentosoptions
Podemos pasar un objeto de configuración que cuenta con multiples opcionescwd
(string) Definir el directorio de trabajo.env
(Object) Variables de entorno (clave-valor).stdio
(Array|string) Opciones para la comunicacion padre-hijo con el streamoptions.stdio
.detached
(boolean) Permite que el hijo se ejecute de forma independiente al padre. Cambia según la plataformaoptions.detached
.uid
(number) Define el UID del proceso. Doc.gid
(number) Define el GID del proceso. Doc.shell
(boolean|string) si es true, ejecuta el comando dentro de una shell./bin/sh
(UNIX) oprocess.env.ComSpec
(Windows). Se puede definir otra shell distintawindowsVerbatimArguments
(boolean) No se citan ni se escapan argumentos en Windows. No aplicable a platformas. Default: false.execPath
(string) Executable used to create the child process.execArgv
(string[]) List of string arguments passed to the executable. Default: process.execArgv.silent
(boolean) Si es true,stdin
,stdout
ystderr
del hijo se pipearán al padre. De ser false se heredan del padre. Default: false.
- Retorna (
ChildProcess
)
Ejemplo
padre.js
const { fork } = require('child_process');
const hijo = fork('hijo.js');
hijo.on('message', (msg) => {
console.log('[hijo] hora:', msg);
});
hijo.send({ data: 'Hola!' });
hijo.js
process.on('message', (msg) => {
console.log('[padre]', msg);
});
setInterval(() => {
process.send({ tiempo: new Date().getTime()});
}, 1000);
Generando pipes
manualmente con child_process.spawn()
ps ax | grep ssh
const { spawn } = require('child_process');
const ps = spawn('ps', ['ax']);
const grep = spawn('grep', ['ssh']);
ps.stdout.on('data', (data) => {
grep.stdin.write(data);
});
ps.stderr.on('data', (data) => {
console.log(`ps stderr: ${data}`);
});
ps.on('close', (code) => {
if (code !== 0) {
console.log(`ps process exited with code ${code}`);
}
grep.stdin.end();
});
grep.stdout.on('data', (data) => {
console.log(data.toString());
});
grep.stderr.on('data', (data) => {
console.log(`grep stderr: ${data}`);
});
grep.on('close', (code) => {
if (code !== 0) {
console.log(`grep process exited with code ${code}`);
}
});
Manejando hijos
const { spawn } = require('child_process');
if(process.argv[2] === 'hijo'){
console.log('Estoy dentro del proceso hijo');
} else {
const hijo = spawn(process.execPath, [__filename, 'hijo'])
hijo.stdout.pipe(process.stdout)
}
Manejando hijos y herencia
const { spawn } = require('child_process');
if(process.argv[2] === 'hijo'){
console.log('Estoy dentro del proceso hijo');
} else {
const hijo = spawn(process.execPath, [__filename, 'hijo'], {
stdio: 'inherit'
})
}
Manejando hijos y contexto común
const { spawn } = require('child_process');
let 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 {
const hijo = spawn(process.execPath, [__filename, 'hijo'], {
stdio: 'inherit'
});
console.log('padre', contador);
}
A single instance of Node.js runs in a single thread. To take advantage of multi-core systems, the user will sometimes want to launch a cluster of Node.js processes to handle the load.
The cluster module allows easy creation of child processes that all share server ports. Nodejs
Sin usar Cluster
const http = require('http');
const url = require('url');
const server = http.createServer().listen(process.env.PORT);
server.on('request', (req, res) => {
const 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"
const cluster = require('cluster'); // nproc
const http = require('http');
const url = require('url');
const cpus = require('os').cpus().length;
if (cluster.isMaster) {
console.log('Proceso maestro con PID:', process.pid);
for (let i = 0; i < cpus; i++) {
cluster.fork();
}
cluster.on('exit', worker => {
console.log(`hijo con PID ${worker.process.pid} muerto`);
cluster.fork();
});
} else {
console.log('Arrancado hijo con PID:', process.pid);
const server = http.createServer().listen(process.env.PORT);
server.on('request', (req, res) => {
const 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}`);
}
});
}
Recursos
- Scaling Node.js Applications
- Taking Advantage of Multi-Processor Environments in Node.js
- Modo cluster para node.js
- How to Create a Node.js Cluster for Speeding Up Your Apps
- How to scale your Node.js server using clustering
- Clustering in NodeJs — Performance Optimization
- Understanding the NodeJS cluster module
Librerias:
I’m a JavaScript developer, why would I ever want to mess with C++?
First: direct access to existing (legacy) C/C++ libraries. Instead of calling these as external applications in “execute command” style, get your hands directly on the existing source code and pass the results back to Node.js in a form that is comprehensible for your JavaScript runtime. This way you can also get an access to low-level API of the operating system.
Second: performance. In many situations, a well-written native code might prove faster and more performant than the JavaScript equivalent. Marcin Baraniecki
Meme Time
Esquema
Funcionamiento
- Inicializas el proyeto:
npm init
- Instalas dependencias:
npm install nan node-gyp --save
- Añades el compilador a los
scripts
delpackage.json
:"compile": “node-gyp rebuild”
- Generas el fichero
bindings.gyp
que define el proceso de compilación{ "targets": [ { "include_dirs": [ "<!(node -e \"require('nan')\")" ], "target_name": "addon", "sources": [ "main.cpp" ] } ] }
npm run compile
genera la compilación de C++ en/build
- Nota: Debes tener instalado
make
,g++
,python 2.7
, etc...node-gyp
te avisa durante la instalación si alguna faltara. - Nota: Repo de ejemplo
main.cpp
#include <nan.h>
// NAN_METHOD is a Nan macro enabling convenient way of creating native node functions.
// It takes a method's name as a param. By C++ convention, I used the Capital cased name.
NAN_METHOD(Hello) {
// Create an instance of V8's String type
auto message = Nan::New("Hello from C++!").ToLocalChecked();
// 'info' is a macro's "implicit" parameter - it's a bridge object between C++ and JavaScript runtimes
// You would use info to both extract the parameters passed to a function as well as set the return value.
info.GetReturnValue().Set(message);
}
// Module initialization logic
NAN_MODULE_INIT(Initialize) {
// Export the `Hello` function (equivalent to `export function Hello (...)` in JS)
NAN_EXPORT(target, Hello);
}
// Create the module called "addon" and initialize it with `Initialize` function (created with NAN_MODULE_INIT macro)
NODE_MODULE(addon, Initialize);
main.js
// note that the compiled addon is placed under following path
const {Hello} = require('./build/Release/addon');
// `Hello` function returns a string, so we have to console.log it!
console.log(Hello());
isPrime.js
module.exports = (number) => {
if (typeof number !== 'number') {
throw new TypeError('argument must be a number!');
}
if (number < 2) {
return false;
}
for (let i = 2; i < number; i++) {
if (number % i === 0) {
return false;
}
}
return true;
};
main.cpp
#include <nan.h>
NAN_METHOD(IsPrime) {
if (!info[0]->IsNumber()) {
Nan::ThrowTypeError("argument must be a number!");
return;
}
int number = (int) info[0]->NumberValue();
if (number < 2) {
info.GetReturnValue().Set(Nan::False());
return;
}
for (int i = 2; i < number; i++) {
if (number % i == 0) {
info.GetReturnValue().Set(Nan::False());
return;
}
}
info.GetReturnValue().Set(Nan::True());
}
NAN_MODULE_INIT(Initialize) {
NAN_EXPORT(target, IsPrime);
}
NODE_MODULE(addon, Initialize);
main.js
const {IsPrime} = require('./build/Release/addon'); // native c++
const isPrime = require('./isPrime'); // js
const number = 654188429; // thirty-fifth million first prime number (see https://primes.utm.edu/lists/small/millions/)
const NATIVE = 'native';
const JS = 'js';
console.time(NATIVE);
console.log(`${NATIVE}: checking whether ${number} is prime... ${IsPrime(number)}`);
console.timeEnd(NATIVE); //2161.723ms
console.log('');
console.time(JS);
console.log(`${JS}: checking whether ${number} is prime... ${isPrime(number)}`);
console.timeEnd(JS); // 3090.118ms
npm run compile
npm start
Opciones Core
Librerías
- node-gyp
- node-bindings Helper module for loading your native module's .node file
- node-gyp Node.js native addon build tool
- nan Native Abstractions for Node.js
Recursos
- How to call C/C++ code from Node.js
- Extending Node.js with native C++ modules
- JavaScript ♥ C++: Modern Ways to Use C++ in JavaScript Projects
- NodeJS Advanced — How to create a native add-on using C++
- Writing cross-platform C++ is easier in Node.js than it is outside of Node.js
- Native Extensions for Node.js
- N-API: Next generation APIs for Node.js native addons available across all LTS release lines