SlideShare ist ein Scribd-Unternehmen logo
1 von 397
Downloaden Sie, um offline zu lesen
Pero, ¿Qué es Node.js?
¿Qué es Node.js?
Lo que todos sabemos

• Hay Javascript por alguna parte
• Backend
• ¿Algo que ver con NoSQL?
• Sirve para hacer chats
¿Qué es Node.js?
Lo que no está tan claro

• ¿Es un framework para Javascript?
• ¿Es una librería?
• ¿Qué tiene que ver v8 con Node.js?
• ¿Para qué sirve (además de los chats)?
¿Qué es Node.js?
Node.js es:

• “Una plataforma de software usada para construir
aplicaciones de red escalables (especialmente servidores).
Node.js utiliza JavaScript como lenguaje, y alcanza alto
rendimiento utilizando E/S no bloqueante y un bucle de
eventos de una sola hebra”.
¿Qué es Node.js?
Node.js es:

• “Una plataforma de software usada para construir
aplicaciones de red escalables (especialmente servidores).
Node.js utiliza JavaScript como lenguaje, y alcanza alto
rendimiento utilizando E/S no bloqueante y un bucle de
eventos de una sola hebra”.

• Wat?
¿Qué es Node.js?
Por ejemplo, si...

• ...para ruby tenemos Rails...
• ...para python tenemos Django...
• ...para php tenemos Symphony...
¿Qué es Node.js?
Por ejemplo, si...

• ...para ruby tenemos Rails...
• ...para python tenemos Django...
• ...para php tenemos Symphony...
• ¿Podríamos decir que Node.js es el equivalente para
JavaScript?
¿Qué es Node.js?

¡¡NO!!
¿Qué es Node.js?
¿Qué es un “lenguaje de programación”?
¿Qué es Node.js?
¿Qué es un “lenguaje de programación”?

• Una gramática que define la sintaxis del lenguaje
• Un intérprete/compilador que lo sabe interpretar y
ejecutar
¿Qué es Node.js?
¿Qué es un “lenguaje de programación”?

• Una gramática que define la sintaxis del lenguaje
• Un intérprete/compilador que lo sabe interpretar y
ejecutar

• Mecanismos para interactuar con el mundo exterior
(llamadas al sistema)
¿Qué es Node.js?
¿Qué es un “lenguaje de programación”?

• Una gramática que define la sintaxis del lenguaje
• Un intérprete/compilador que lo sabe interpretar y
ejecutar

• Mecanismos para interactuar con el mundo exterior
(llamadas al sistema)

• Librería estándar (consola, ficheros, red, etc,...)
¿Qué es Node.js?
¿Qué es un “lenguaje de programación”?

• Una gramática que define la sintaxis del lenguaje
• Un intérprete/compilador que lo sabe interpretar y
ejecutar

• Mecanismos para interactuar con el mundo exterior
(llamadas al sistema)

• Librería estándar (consola, ficheros, red, etc,...)
• Utilidades (intérprete interactivo, depurador,
paquetes)
¿Qué es Node.js?
v8 (JavaScript)

• Una gramática que define la sintaxis del lenguaje
• Un intérprete/compilador que lo sabe interpretar y
ejecutar

• Mecanismos para interactuar con el mundo exterior
(llamadas al sistema)

• Librería estándar (consola, ficheros, red, etc,...)
• Utilidades (intérprete interactivo, depurador, paquetes)
Node.js
¿Qué es Node.js?
Node.js

Ruby

Python

Java

Lenguaje

JavaScript

Ruby

Python

Java

Motor

v8

YARV

cPython

JavaVM

Entorno

Node.js

Ruby
Standard
Library

Python
Standard
Library

Java SE

Framework

???

Rails

Django

Spring
¿Qué es Node.js?
Node.js es algo más:

• Una filosofía sobre cómo hacer las cosas
• Un modelo de ejecución singular
• Muy enfocado hacia aplicaciones de red
¿Qué es Node.js?
Node.js es algo más:

• Una filosofía sobre cómo hacer las cosas
• Un modelo de ejecución singular
• Muy enfocado hacia aplicaciones de red
Una filosofía
Node.js se crea con un objetivo en mente:

• Escribir aplicaciones muy eficientes (E/S) con el
lenguaje dinámico más rápido (v8) para soportar miles
de conexiones simultáneas

• Sin complicarse la vida innecesariamente
- Sin paralelismo
- Lenguaje sencillo y muy extendido
- API muy pequeña y muy consistente
- Apoyándose en Eventos y Callbacks
Una filosofía
Una filosofía
• No es la mejor opción para todos los casos
- Si puedes hacerlo con Rails/Django/Spring, hazlo

• Evita las “soluciones totales”
- Una necesidad, una herramienta
- Combina diferentes herramientas simples

• Entiende lo que estás haciendo
• Flexibilidad > magia
- Tu código es tu responsabilidad
- Cada aplicación es un mundo
¿Qué es Node.js?
Node.js es algo más:

• Una filosofía sobre cómo hacer las cosas
• Un modelo de ejecución singular
• Muy enfocado hacia aplicaciones de red
Un modelo de ejecución
Para entender Node.js, tenemos que entender dos
ideas fundamentales:

• Concurrencia vs. paralelismo (asincronía)
• Eventos
Un modelo de ejecución
Concurrencia vs. Paralelismo

• ¿Qué significa que dos cosas suceden “a la vez”?
Un modelo de ejecución
¿Qué significa que dos cosas suceden “a la vez”?
Un modelo de ejecución
¿Qué significa que dos cosas suceden “a la vez”?
Un modelo de ejecución
Concurrencia vs. Paralelismo

• Paralelismo: varios actores realizando una acción cada
uno simultáneamente

• Concurrencia: un solo actor, con varias tareas “activas”
entre las que va alternando
Un modelo de ejecución
Paralelismo
Un modelo de ejecución
Concurrencia
Un modelo de ejecución
La potencia de Node.js es (curiosamente):

• Un modelo de ejecución concurrente
- Muchos clientes o tareas activas

• Pero NO paralelo
- Una única hebra
Un modelo de ejecución
• ¿Qué ventajas tiene evitar el paralelismo?
• ¿Qué desventajas?
• Y por tanto, ¿Cuándo es útil el modelo de Node.js?
Un modelo de ejecución
Patrón Reactor

• Un patrón de diseño para manejar eventos donde peticiones
de servicio se transladan concurrentemente a un
manejador central que se encarga de desmultiplexar las
peticiones y despacharlas síncronamente mediante sus
manejadores particulares asociados.
Un modelo de ejecución
Patrón Reactor

• Un patrón de diseño para manejar eventos donde peticiones
de servicio se transladan concurrentemente a un
manejador central que se encarga de desmultiplexar las
peticiones y despacharlas síncronamente mediante sus
manejadores particulares asociados.

• WAT??
Un modelo de ejecución
Patrón Reactor

Bucle Principal
Un modelo de ejecución
Patrón Reactor

Bucle Principal

Mundo Exterior
Un modelo de ejecución
Patrón Reactor

Suceso

Bucle Principal

Mundo Exterior
Un modelo de ejecución
Patrón Reactor

Bucle Principal
Suceso

Mundo Exterior
Un modelo de ejecución
Patrón Reactor

Suceso 2

Bucle Principal
Suceso

Mundo Exterior
Un modelo de ejecución
Patrón Reactor

Bucle Principal
Suceso
Suceso 2

Mundo Exterior
Un modelo de ejecución
Patrón Reactor

Tick!
Bucle Principal
Suceso
Suceso 2

Mundo Exterior
Un modelo de ejecución
Patrón Reactor

Tick!
Bucle Principal
Suceso
Suceso 2

Mundo Exterior

Evento:
“Suceso”
Un modelo de ejecución
Patrón Reactor

Tick!

Evento:
“Suceso”

Bucle Principal
Suceso
Suceso 2

Mundo Exterior

Manejadores
Un modelo de ejecución
Patrón Reactor

Tick!
Bucle Principal
Suceso
Suceso 2

Mundo Exterior

Manejadores
Un modelo de ejecución
Patrón Reactor

Suceso 3

Tick!
Bucle Principal
Suceso
Suceso 2

Mundo Exterior

Manejadores
Un modelo de ejecución
Patrón Reactor

Tick!
Bucle Principal
Suceso
Suceso 2

Mundo Exterior

Suceso 3

Manejadores
Un modelo de ejecución
Patrón Reactor

Tick!

Listo!

Bucle Principal
Suceso
Suceso 2

Mundo Exterior

Suceso 3

Manejadores
Un modelo de ejecución
Patrón Reactor

Tick!
Bucle Principal
Suceso
Suceso 2

Mundo Exterior

Suceso 3

Manejadores
Un modelo de ejecución
Patrón Reactor

Tick!

Evento:
“Suceso 2”

???

Bucle Principal
Suceso
Suceso 2

Mundo Exterior

Suceso 3

Manejadores
Un modelo de ejecución
Patrón Reactor

Bucle Principal
Suceso
Suceso 2

Mundo Exterior

Suceso 3

Manejadores
Un modelo de ejecución
Patrón Reactor

• Programación contra eventos
• Una vez puestos los manejadores, se pueden ejecutar
en cualquier orden

- El orden lo determina el orden en que aparezcan sucesos

• La ejecución de los manejadores bloquea la hebra
• Nunca hay dos manejadores ejecutándose al mismo
tiempo

• Muy eficiente... cuando E/S >> ejecuión del manejador
¿Qué es Node.js?
Node.js es algo más:

• Una filosofía sobre cómo hacer las cosas
• Un modelo de ejecución singular
• Muy enfocado hacia aplicaciones de red
¿Qué es Node.js?
Muy enfocado hacia aplicaciones de red

• ¿Por qué?
¿Qué es Node.js?
Muy enfocado hacia aplicaciones de red

• Mucha E/S
- Por tanto, mucho tiempo con la CPU inactiva

• Para aprovechar ese tiempo, necesitas otros clientes
que lo puedan aprovechar

• Similar a un camarero en un bar
- Es un “Patron Reactor” del mundo real
- Para aprovecharlo, tiene que haber varios clientes!
- Un cliente no termina más deprisa por dedicarle un camarero
sólo a él
Toma de contacto
Ahora en serio: ¿Qué es Node.js?
Necesitas:

• Un ordenador
• Un editor de texto
• Saber abrir una consola/terminal
• Tener node instalado (mejor si es v. 0.10)
- Asegúrate de tener también npm
- Como alternativa: http://c9.io

• Saber manejarte con JavaScript
Ahora en serio: ¿Qué es Node.js?

console.log("Hola, Mundo!");
Ahora en serio: ¿Qué es Node.js?

$ node hola.js
Ahora en serio: ¿Qué es Node.js?
Ahora en serio: ¿Qué es Node.js?
Pero...

• ¿Y ese rollo del patrón Reactor?
• ¿Por qué termina el programa en vez de quedarse
escuchando sucesos en el bucle principal?
Ahora en serio: ¿Qué es Node.js?
Lo que pasa en realidad:

• Node.js ejecuta todo tu código del tirón
• Coloca los manejadores que hayas definido
• Si no hay ningún manejador que se pueda ejecutar en
el futuro, el programa termina!
Ahora en serio: ¿Qué es Node.js?
setTimeout(function() {
console.log("Hola, Mundo del futuro!");
}, 1000);
Ahora en serio: ¿Qué es Node.js?
setInterval(function() {
console.log("Hola otra vez, Mundo del futuro!");
}, 1000);
Ahora en serio: ¿Qué es Node.js?
setTimeout(function() {
while (true);
}, 100);
setInterval(function() {
console.log("Hola otra vez, Mundo del futuro!");
}, 1000);
Ahora en serio: ¿Qué es Node.js?
var start = Date.now();
setTimeout(function() {
for (var i=Number.MAX_VALUE; i--;) {
Math.pow(12345, 123455);
}
}, 100);
setInterval(function() {
var now = Date.now();
console.log("Han pasado", now - start, "ms");
start = now;
console.log("Hola otra vez, Mundo del futuro!");
}, 1000);
Ahora en serio: ¿Qué es Node.js?
var start = Date.now();
setTimeout(function() {
var timesLeft = Number.MAX_VALUE,
r;
(function unPoquitoMas() {
if (timesLeft-- > 0) { r = Math.pow(12345, 123455); }
setTimeout(unPoquitoMas, 0);
}());
}, 100);
setInterval(function() {
var now = Date.now();
console.log("Han pasado", now - start, "ms");
start = now;
console.log("Hola otra vez, Mundo del futuro!");
}, 1000);
Ahora en serio: ¿Qué es Node.js?
Nos surgen problemas curiosos...

• ¿Excepciones?
Ahora en serio: ¿Qué es Node.js?
Nos surgen problemas curiosos...

• ¿Excepciones?
try {
throw new Error("Peté!");
} catch(e) {
console.log("Excepción!");
}
Ahora en serio: ¿Qué es Node.js?
Nos surgen problemas curiosos...

• ¿Excepciones?
try {
setTimeout(function() {
throw new Error("Peté!");
}, 0);
} catch(e) {
console.log("Excepción!");
}
EventEmitter
Nuestro código va a estar dirigido por eventos

• Node.js tiene su propio “estándar”
• Trae una implementación del patrón Observador (o
Pub/Sub): EventEmitter

• Todas sus librerías (y casi todos los paquetes) siguen
este modelo
EventEmitter
var EventEmitter = require("events").EventEmitter;
var pub = new EventEmitter();
pub.on("ev", function(m) {
console.log("[ev]", m);
});
pub.once("ev", function(m) {
console.log("(ha sido la primera vez)");
});
pub.emit("ev", "Soy un Emisor de Eventos!");
pub.emit("ev", "Me vas a ver muy a menudo...");
EventEmitter
var EventEmitter = require("events").EventEmitter;
var pub = new EventEmitter();
pub.on("ev", function(m) {
console.log("[ev]", m);
});
pub.once("ev", function(m) {
console.log("(ha sido la primera vez)");
});
pub.emit("ev", "Soy un Emisor de Eventos!");
pub.emit("ev", "Me vas a ver muy a menudo...");
require/exports
require(<paquete o ruta>)

• Importar módulos (paquetes, otros ficheros)
• Garantía: una única vez
• Devuelve el módulo!
require/exports
exports.propiedadPublica = <valor>

• El otro lado del mecanismo
• Se puede exportar cualquier valor
require/exports
codigo.js
var lib = require("./libreria");
console.log(lib.propiedad);

libreria.js
console.log("una vez");
exports.propiedad = "Pública";
Para que te confíes...
Haz un módulo “reloj”

• Que exporte una clase Reloj
• Emita eventos “segundo”, “minuto” y “hora”
var Reloj = require("./reloj").Reloj;
var reloj = new Reloj();
reloj.on("segundo", function(fecha) {
console.log("Un segundo! son las:", fecha);
reloj.removeAllListeners("segundo");
});
Para que te confíes...
Un truco:
•
•

require(“util”).inherits
inherits(constructor, superConstructor)
var inherits = require("util").inherits;
function MiClase() {
// ...
}
function MiSubClase() {
// ...
}
inherits(MiSubClase, MiClase);
JavaScript y el Universo
El JavaScript del navegador vive en un mundo ideal

• No hay SO con el que lidiar
• No hay datos binarios
• Todo es accesible con objetos y valores primitivos
• Apenas hay E/S, siempre con valores simples (strings)
• Un único usuario
JavaScript y el Universo
En Node.js, las cosas son de otra manera...

• Llamadas al sistema
• Mucha E/S
• Datos binarios (ficheros, sockets, etc)
• Descriptores de ficheros
• Puertos
• ...
JavaScript y el Universo
Tenemos que añadir nuevos conceptos a nuestro JS

• Streams (lectura, escritura o duplex)
• Buffers (representación de datos binarios)
• Procesos
• Rutas
• http://nodejs.org/api/index.html
JavaScript y el Universo
Buffers

• Una tira de bytes (datos binarios)
• Similar a un array de enteros
• Tamaño fijo
• Manipular datos directamente
- Sockets
- Implementar protocolos complejos
- Manipulación de ficheros/imágenes
- Criptografía
- ...
JavaScript y el Universo
Buffers
var buf = new Buffer(100);
buf.write("abcd", 0, 4, "ascii");
console.log(buf.toString("ascii"));
JavaScript y el Universo
Buffers

Tamaño del buffer

var buf = new Buffer(100);
buf.write("abcd", 0, 4, "ascii");
console.log(buf.toString("ascii"));
JavaScript y el Universo
Posición

Buffers

Datos

Longitud
Codificación

var buf = new Buffer(100);
buf.write("abcd", 0, 4, "ascii");
console.log(buf.toString("ascii"));
JavaScript y el Universo
Buffers
var buf = new Buffer(100);
buf.write("abcd", 0, 4, "ascii");
console.log(buf.toString("ascii"));

Codificación
JavaScript y el Universo
Buffers
JavaScript y el Universo
function Bitmap(w, h) {
this.width = w;
this.height = h;
this.header = "P6n" + w + " " + h + "n255n";
this.buffer = new Buffer(w*h*3+this.header.length);
this.buffer.write(this.header, 0, this.header.length, "ascii");
}
Bitmap.prototype = {
putPixel: function(x, y, color) {
var pos = this.header.length + (y*this.width*3) + x*3;
this.buffer.write(color, pos, 3, "hex");
},
fill: function(color) {
this.buffer.fill(255, this.header.length);
},
render: function() {
process.stdout.write(this.buffer);
}
};
JavaScript y el Universo
var bitmap = new Bitmap(1, 1);
bitmap.putPixel(0, 0, "000000");
bitmap.render();
JavaScript y el Universo
var bitmap = new Bitmap(10, 10);
bitmap.fill("ffffff");
bitmap.putPixel(0, 0, "000000");
bitmap.render();
JavaScript y el Universo
Streams

• “Chorros” de información
- Lectura / Escritura / Duplex

• Detrás de muchos mecanismos de Node.js
- stdin/stdout
- request HTTP
- sockets
- etc...

• Instancias de EventEmitter
• Acceso asíncrono
JavaScript y el Universo
Streams

• Es raro crear streams directamente
• Pero muchos recursos nos ofrecen este interfaz
JavaScript y el Universo
Streams de lectura

• Entrada de datos
• Eventos:
- readable: hay datos para leer
- data: se ha leído un trozo y está disponible
- end: se agotó el stream
- close: se cerró el stream
- error: algo malo sucedió leyendo los datos
JavaScript y el Universo
Streams de lectura
var fs = require("fs");
var readStream = fs.createReadStream("/etc/passwd", {
flags: "r",
encoding: "ascii",
autoClose: true
});
readStream.on("data", function(chunk) {
console.log("He leído:", chunk.length);
});
readStream.on("end", function() {
console.log("ya está!");
});
Una fácil
Haz un programa que cuente las líneas de un fichero
Una fácil
Haz un programa que cuente las líneas de un fichero

• Tienes los argumentos con los que se ha llamado al
programa en process.argv
JavaScript y el Universo
Streams de escritura

• Salida de datos
• Operaciones:
-

write(chunk, [encoding], [callback])
end([chunk], [encoding], [callback])

• Eventos:
- drain: el buffer del stream está vacío (puedes escribir más)
- finish: se ha terminado de escribir toda la info y se ha
cerrado el stream
JavaScript y el Universo
Streams de escritura
var fs = require("fs");
var writeStream = fs.createWriteStream(process.argv[2], {
flags: "w",
encoding: "utf-8"
});
for (var i=100; i--;) {
writeStream.write(i + " líneas más para terminar...n");
}
writeStream.end("FIN");
writeStream.on("finish", function() {
console.log("Listo!");
});
¿Preguntas?
Un buen momento para despejar dudas antes de
seguir...
HTTP

(por fin...)
HTTP
Node.js trae un servidor web estupendo

• Asíncrono
- No bloquea la hebra
- Cada cliente conectado consume muy poquitos recursos
- Genial para miles de conexiones simultáneas

• Relativamente rápido
• Interfaz sencilla
• HTTP puro y duro, sin adornos
• Basado en streams y eventos
HTTP
var http = require("http");
var server = http.createServer();
server.on("request", function(req, res) {
res.end("Hola, Mundo!");
});
server.listen(3000);
El módulo

HTTP

var http = require("http");
var server = http.createServer();
server.on("request", function(req, res) {
res.end("Hola, Mundo!");
});
server.listen(3000);
HTTP
var http = require("http");

Eventos!

Respuesta
(stream)

var server = http.createServer();
server.on("request", function(req, res) {
res.end("Hola, Mundo!");
});
server.listen(3000);

Request
(objeto)
HTTP
El servidor HTTP

• Eventos:
-

connection
request

• Operaciones:
-

createServer([requestCallback])
listen(puerto, [hostname], [backlog], [callback])
close([callback])
HTTP
http.IncomingMessage (parametro “req”)

• Representa la petición HTTP del cliente
• Propiedaes:
-

req.headers: cabeceras de la petición

-

req.method: verbo HTTP

-

req.url: url de la petición

-

req.conneciton.remoteAddress: ip del cliente
HTTP
http.ServerResponse (parametro “res”)

• Representa la respuesta del servidor
• Stream de escritura
• Operaciones adicionales:
-

res.writeHead(statusCode, [headers]):

código HTTP y

cabeceras de la respuesta

-

res.statusCode: [propiedad] Otra forma de establecer el

código HTTP de la respuesta

-

res.setHeader(name, value): Otra forma de establecer las

cabeceras, de una en una
Manos a la obra
Escribe un servidor web que devuelva la hora
Un consejo: nodemon
Para mejorar el flow de trabajo:

• Editar, matar el proceso, volver a lanzarlo, probar... muy
tedioso!

• Instálate nodemon
npm install -g nodemon

• Reinicia el servidor cada vez que cambia el fichero
$ nodemon <fichero.js>
HTTP
Node.js trae un módulo para parsear URLs
var http = require("http"),
url = require("url"),
inspect = require("util").inspect;
var server = http.createServer();
server.on("request", function(req, res) {
var urlData = url.parse(req.url, true);
res.end(inspect(urlData, {colors: false}));
});
server.listen(3000);
HTTP
Node.js trae un módulo para parsear URLs
var http = require("http"),
url = require("url"),
inspect = require("util").inspect;
var server = http.createServer();
server.on("request", function(req, res) {
var urlData = url.parse(req.url, true);
res.end(inspect(urlData, {colors: false}));
});
server.listen(3000);
HTTP
Un poco más difícil
Escribe un servidor de ficheros

• Lee el pathname de la URL
• Busca un fichero con esa ruta dentro de ./public
• Si existe, lo sirve
• Si no existe, devuelve 404
Un poco más difícil: notas
fs.exists(filePath, callback)

• Llama al callback con true si el fichero filePath existe
• O con false si no existe
var fs = require("fs");
fs.exists("./hola.txt", function(exists) {
console.log(exists);
});
Un poco más difícil: notas
fs.readFile(filePath, callback)

• Otra manera de leer ficheros
• Intenta leer filePath e invoca a callback con dos
parámetros:
-

err: null si todo ha ido bien o, si hubo error, el error

-

data: todo el contenido del fichero (si fue posible leerlo)

var fs = require("fs");
fs.readFile("./hola.txt", function(err, data) {
if (err) { /* error! */ }
console.log(data);
});
Un poco más difícil: epílogo
¿Cómo podríamos añadir un caché para no leer los
ficheros del disco duro más de una vez?
¿Cómo podríamos hacer que los ficheros cacheados
se liberaran después de x minutos?
¿Cómo podríamos escribir un registro de acceso?
Un poco más difícil: variaciones
Haz un contador de aperturas de emails

• Sirviendo un .gif de 1x1 y contando cuantas veces lo
sirves

• Mejor aún, cuenta solo a cuántas IPs lo sirves
Haz un servicio de avatares que cambie según:

• La hora del día
• La frecuencia con que es pedido (popularidad)
Un poco más difícil: variaciones
Haz un “servidor hellban”
Si la IP está en la lista negra, todas las peticiones tienen
un delay aleatorio que se va incrementando con cada
petición consecutiva
Servidor A/B Testing
Cada vez que un cliente pide un recurso:

• Se comprueba si ya tiene caso asignado (por IP) o se le
asigna uno

• Se construye la ruta del recurso según el caso asignado
y se sirve

• Si pide “success.png”, se marca su caso como exitoso y
se le sirve la imágen

• /stats devuelve un JSON con info sobre los casos
(visitas totales y visitas exitosas por cada caso)
Servidor A/B Testing
Tenéis maqueta y recursos en :
/tema1/abtesting
Promesas
Node.js y CPS
Node.js maneja la asincronía utilizando callbacks

• Continuation Passing Style
• Continuaciones explícitas como funciones
• “Cuando termines, ejecuta esta otra función”
Node.js y CPS
Los callbacks tienen muchas ventajas

• Muy fáciles de entender e implementar
• Familiares para el programador JavaScript
• Extremadamente flexibles (clausuras, funciones de
primer orden, etc, ...)

• Un mecanismo universal de asincronía/continuaciones
Pero...
Node.js y CPS
var fs = require("fs");
fs.exists("./hola.txt", function(exists) {
if (exists) {
fs.readFile("./hola.txt", function(err, data) {
if (err) {
// MANEJO DE ERROR
} else {
fs.writeFile("./copia.txt", data, function(err) {
if (err) {
// MANEJO DE ERROR
} else {
console.log("OK!");
}
})
}
})
} else {
// MANEJO DE ERROR
}
});
Node.js y CPS
var fs = require("fs");
fs.exists("./hola.txt", function(exists) {
if (exists) {
fs.readFile("./hola.txt", function(err, data) {
if (err) {
// MANEJO DE ERROR
} else {
fs.writeFile("./copia.txt", data, function(err) {
if (err) {
// MANEJO DE ERROR
} else {
console.log("OK!");
}
})
}
})
} else {
// MANEJO DE ERROR
}
});
Node.js y CPS
var fs = require("fs");
fs.exists("./hola.txt", function(exists) {
if (exists) {
fs.readFile("./hola.txt", function(err, data) {
if (err) {
// MANEJO DE ERROR
} else {
fs.writeFile("./copia.txt", data, function(err) {
if (err) {
// MANEJO DE ERROR
} else {
console.log("OK!");
}
})
}
})
} else {
// MANEJO DE ERROR
}
});
Node.js y CPS
var fs = require("fs");
fs.exists("./hola.txt", function(exists) {
if (exists) {
fs.readFile("./hola.txt", function(err, data) {
if (err) {
// MANEJO DE ERROR
} else {
fs.writeFile("./copia.txt", data, function(err) {
if (err) {
// MANEJO DE ERROR
} else {
console.log("OK!");
}
})
}
})
} else {
// MANEJO DE ERROR
}
});

Pyramid of Doom
Callback Hell
Node.js y CPS
var fs = require("fs");
fs.exists("./hola.txt", function(exists) {
if (exists) {
fs.readFile("./hola.txt", function(err, data) {
if (err) {
// MANEJO DE ERROR
} else {
fs.writeFile("./copia.txt", data, function(err) {
if (err) {
// MANEJO DE ERROR
} else {
console.log("OK!");
}
})
}
})
} else {
// MANEJO DE ERROR
}
});
CPS vs. Promesas
Promesas
Una manera alternativa de modelar asincronía

• Construcción explícita del flujo de ejecución
• Separación en bloques consecutivos
• Manejo de errores más controlado
• Combinación de diferentes flujos asíncronos
Promesas
Una promesa = Un flujo de ejecución
promesa.then(function() {
// bloque
return readFilePromise("./hola.txt");
})
.then(function(data) {
// bloque
return writeFilePromise("./copia.txt");
})
.then(function() {
console.log("listo!");
})
.fail(function(err) {
// MANEJO DEL ERROR
});
Promesas
Una promesa = Un flujo de ejecución
promesa.then(function() {
// bloque
return readFilePromise("./hola.txt");
})
.then(function(data) {
// bloque
return writeFilePromise("./copia.txt");
})
.then(function() {
console.log("listo!");
})
.fail(function(err) {
// MANEJO DEL ERROR
});
Promesas
Una promesa = Un flujo de ejecución
promesa.then(function() {
// bloque
return readFilePromise("./hola.txt");
})
.then(function(data) {
// bloque
return writeFilePromise("./copia.txt");
})
.then(function() {
console.log("listo!");
})
.fail(function(err) {
// MANEJO DEL ERROR
});
Promesas
Una promesa = Un flujo de ejecución
promesa.then(function() {
// bloque
return readFilePromise("./hola.txt");
})
.then(function(data) {
// bloque
return writeFilePromise("./copia.txt");
})
.then(function() {
console.log("listo!");
})
.fail(function(err) {
// MANEJO DEL ERROR
});
Promesas
Una promesa = Un flujo de ejecución
promesa.then(function() {
// bloque
return readFilePromise("./hola.txt");
})
.then(function(data) {
// bloque
return writeFilePromise("./copia.txt");
})
.then(function() {
console.log("listo!");
})
.fail(function(err) {
// MANEJO DEL ERROR
});
Promesas
Una promesa = Un flujo de ejecución
promesa.then(function() {
// bloque
return readFilePromise("./hola.txt");
})
.then(function(data) {
// bloque
return writeFilePromise("./copia.txt");
})
.then(function() {
console.log("listo!");
})
.fail(function(err) {
// MANEJO DEL ERROR
});
Promesas
Una promesa = Un flujo de ejecución
promesa.then(function() {
// bloque
return readFilePromise("./hola.txt");
})
.then(function(data) {
// bloque
return writeFilePromise("./copia.txt");
})
.then(function() {
console.log("listo!");
})
.fail(function(err) {
// MANEJO DEL ERROR
});
Promesas
Una promesa = Un flujo de ejecución
promesa.then(function() {
// bloque
return readFilePromise("./hola.txt");
})
.then(function(data) {
// bloque
return writeFilePromise("./copia.txt");
})
.then(function() {
console.log("listo!");
})
.fail(function(err) {
// MANEJO DEL ERROR
});
Promesas
Una promesa = Un flujo de ejecución
promesa.then(function() {
// bloque
return readFilePromise("./hola.txt");
})
.then(function(data) {
// bloque
return writeFilePromise("./copia.txt");
})
.then(function() {
console.log("listo!");
})
.fail(function(err) {
// MANEJO DEL ERROR
});
Promesas
Una promesa = Un flujo de ejecución
Promesas
¡Pero aún no hemos ejecutado nada! Solamente
hemos construído el flujo
Promesas
¿Ventajas?

• Código mucho más ordenado y más legible
• Mejor control de errores
• Podemos manipular el flujo
- Añadir nuevas etapas
- Devolverlo en funciones
- Pasarlo como parámetro

• Podemos combinar varios flujos
Promesas
function copyFile(from, to) {
return readFilePromise(from);
.then(function(data) {
// bloque
return writeFilePromise(to);
});
}
copyFile("./hola.txt", "./copia.txt")
.then(function() {
return copyFile("./otraCosa.txt", "./copia2.txt");
})
.then(function() {
console.log("listo!");
})
.fail(function(err) {
console.log("Oops!");
})
Promesas
function copyFile(from, to) {
return readFilePromise(from);
.then(function(data) {
// bloque
return writeFilePromise(to);
});
}
copyFile("./hola.txt", "./copia.txt")
.then(function() {
return copyFile("./otraCosa.txt", "./copia2.txt");
})
.then(function() {
console.log("listo!");
})
.fail(function(err) {
console.log("Oops!");
})
Promesas
function copyFile(from, to) {
return readFilePromise(from);
.then(function(data) {
// bloque
return writeFilePromise(to);
});
}
copyFile("./hola.txt", "./copia.txt")
.then(function() {
return copyFile("./otraCosa.txt", "./copia2.txt");
})
.then(function() {
console.log("listo!");
})
.fail(function(err) {
console.log("Oops!");
})
Promesas
function copyFile(from, to) {
return readFilePromise(from);
.then(function(data) {
// bloque
return writeFilePromise(to);
});
}
copyFile("./hola.txt", "./copia.txt")
.then(function() {
return copyFile("./otraCosa.txt", "./copia2.txt");
})
.then(function() {
console.log("listo!");
})
.fail(function(err) {
console.log("Oops!");
})
Promesas
function copyFile(from, to) {
return readFilePromise(from);
.then(function(data) {
// bloque
return writeFilePromise(to);
});
}
copyFile("./hola.txt", "./copia.txt")
.then(function() {
return copyFile("./otraCosa.txt", "./copia2.txt");
})
.then(function() {
console.log("listo!");
})
.fail(function(err) {
console.log("Oops!");
})
Promesas
function copyFile(from, to) {
return readFilePromise(from);
.then(function(data) {
// bloque
return writeFilePromise(to);
});
}
copyFile("./hola.txt", "./copia.txt")
.then(function() {
return copyFile("./otraCosa.txt", "./copia2.txt");
})
.then(function() {
console.log("listo!");
})
.fail(function(err) {
console.log("Oops!");
})
Promesas
function copyFile(from, to) {
return readFilePromise(from);
.then(function(data) {
// bloque
return writeFilePromise(to);
});
}
copyFile("./hola.txt", "./copia.txt")
.then(function() {
return copyFile("./otraCosa.txt", "./copia2.txt");
})
.then(function() {
console.log("listo!");
})
.fail(function(err) {
console.log("Oops!");
})
Promesas
function copyFile(from, to) {
return readFilePromise(from);
.then(function(data) {
// bloque
return writeFilePromise(to);
});
}
copyFile("./hola.txt", "./copia.txt")
.then(function() {
return copyFile("./otraCosa.txt", "./copia2.txt");
})
.then(function() {
console.log("listo!");
})
.fail(function(err) {
console.log("Oops!");
})
Promesas
.then(success, [error])

• Concatena bloques
• El nuevo bloque (success)...
- Sólo se ejecuta si el anterior se ha ejecutado sin errores
- Recibe como parámetro el resultado del bloque anterior
- Devuelve el valor que se le pasará el siguiente bloque
➡ Si es un dato inmediato, se pasa tal cual
➡ Si es una promesa, se resuelve antes de llamar al siguiente bloque

• El segundo parámetro pone un manejador de error
- Equivalente a llamar a .fail(error)

• .then(...) siempre devuelve una nueva promesa
Promesas

var promesa = readFilePromise("./hola.txt");
promesa = promesa.then(function(data) {
console.log("Contenido del fichero: ", data);
}, function(err) {
console.log("Ooops!", err);
})
Promesas

var promesa = readFilePromise("./hola.txt");
promesa = promesa.then(function(data) {
console.log("Contenido del fichero: ", data);
}, function(err) {
console.log("Ooops!", err);
})
Promesas

var promesa = readFilePromise("./hola.txt");
promesa = promesa.then(function(data) {
console.log("Contenido del fichero: ", data);
}, function(err) {
console.log("Ooops!", err);
})
Promesas

var promesa = readFilePromise("./hola.txt");
promesa = promesa.then(function(data) {
console.log("Contenido del fichero: ", data);
}, function(err) {
console.log("Ooops!", err);
})
Promesas

var promesa = readFilePromise("./hola.txt");
promesa = promesa.then(function(data) {
console.log("Contenido del fichero: ", data);
}, function(err) {
console.log("Ooops!", err);
})
Promesas

var promesa = readFilePromise("./hola.txt");
promesa = promesa.then(function(data) {
console.log("Contenido del fichero: ", data);
}, function(err) {
console.log("Ooops!", err);
})
Promesas

var promesa = readFilePromise("./hola.txt");
promesa = promesa.then(function(data) {
console.log("Contenido del fichero: ", data);
}, function(err) {
console.log("Ooops!", err);
})
Promesas
var promesa = readFilePromise("./hola.txt");
promesa.then(function(data) {
return 1;
})
.then(function(uno) {
return 2;
}, function(err) {
console.log("Oh, oh...");
})
.then(function(dos) {
return 3;
})
.fail(function(err) {
console.log("Oops!");
});

data

1

2

3
Promesas
var promesa = readFilePromise("./hola.txt");
var promesa2 = promesa.then(function(data) {
return 1;
});
var promesa3 = promesa.then(function(data) {
return 2;
});

data

1

data

2
Promesas
var promesa = readFilePromise("./hola.txt");
var promesa2 = promesa.then(function(data) {
return 1;
});
var promesa3 = promesa.then(function(data) {
return 2;
});
promesa3.then(function(dos) {
console.log("Ping!");
});

data

1

data

2
Promesas
var promesa = readFilePromise("./hola.txt");
var promesa2 = promesa.then(function(data) {
return 1;
});
var promesa3 = promesa.then(function(data) {
return 2;
});
promesa3.then(function(dos) {
console.log("Ping!");
});
promesa3.then(function(dos) {
console.log("Pong!");
});

data

1

data

2

2
Promesas
var promesa = readFilePromise("./hola.txt");
var promesa2 = promesa.then(function(data) {
return 1;
});
var promesa3 = promesa.then(function(data) {
return 2;
});
promesa3.then(function(dos) {
console.log("Ping!");
});
promesa3.then(function(dos) {
console.log("Pong!");
}, function(err) {
console.log("Oh, oh...");
});

data

1

data

2

2
Promesas: walled garden
Vamos a empezar a trastear con promesas...

• Pero, de momento, con una librería de mentira
• Para asentar conceptos
• (y perder el miedo)
Promesas: walled garden
var fakePromise = require("./fakePromise");
var promise = fakePromise.gimmePromise();
promise.then(function() {
return "hola";
})
.then(function(msg) {
console.log(msg);
return "mundo";
})
.then(function(msg) {
console.log(msg);
});
Promesas: walled garden
var fakePromise = require("./fakePromise");
var promise = fakePromise.gimmePromise();
promise.then(function() {
return "hola";
})
.then(function(msg) {
console.log(msg);
return "mundo";
})
.then(function(msg) {
console.log(msg);
});
Promesas: walled garden
var fakePromise = require("./fakePromise");
var promise = fakePromise.gimmePromise();
promise.then(function() {
return "hola";
})
.then(function(msg) {
console.log(msg);
return "mundo";
})
.then(function(msg) {
console.log(msg);
});

¿Qué se muestra por consola al ejecutar esto?
Promesas: walled garden
var fakePromise = require("./fakePromise");
var promise = fakePromise.gimmePromise();
promise.then(function() {
return "hola";
})
.then(function(msg) {
console.log(msg);
return "mundo";
})
.then(function(msg) {
console.log(msg);
});

¿Por qué?
Promesas: walled garden
Una promesa = un flujo de ejecución

• Configuramos un árbol de flujos e ejecución
• Añadimos bloques a promesas
• Pero... ¿Cuándo se empieza a ejecutar?
Promesas: walled garden
Una promesa = un flujo de ejecución

• Configuramos un árbol de flujos e ejecución
• Añadimos bloques a promesas
• Pero... ¿Cuándo se empieza a ejecutar?
• Cuando se resuelva la primera promesa del árbol
Promesas: walled garden
Las promesas se resuelven o se rechazan
Si se resuelven:

• Se resuelven a un valor (si es un bloque, su valor de retorno)
• Representan el estado “OK, puede seguir el siguiente”
Si se rechazan:

• Representan un error
• La ejecución cae hasta el siguiente manejador de errores
• Se saltan todos los estados desde el error hasta el manejador
Promesas: walled garden
var fakePromise = require("./fakePromise");
var promise = fakePromise.gimmePromise();
promise.then(function() {
return "hola";
})
.then(function(msg) {
console.log(msg);
return "mundo";
})
.then(function(msg) {
console.log(msg);
});
promise.resolve(42);
Promesas: walled garden
var fakePromise = require("./fakePromise");
var promise = fakePromise.gimmePromise();
promise.then(function() {
return "hola";
})
.then(function(msg) {
console.log(msg);
return "mundo";
})
.then(function(msg) {
console.log(msg);
})
.fail(function(err) {
console.log(err);
return "MAL!"
})
promise.reject(new Error("Oops!"));
Promesas: walled garden
Otra manera de rechazar una promesa es lanzar una
excepción desde el interior de un bloque
promise.then(function() {
return "hola";
})
.then(function(msg) {
console.log(msg);
throw new Error("Oh, oh...");
return "mundo";
})
Promesas: walled garden
var fakePromise = require("./fakePromise");
var promise = fakePromise.gimmePromise();
promise.then(function() {
return "hola";
})
.then(function(msg) {
console.log(msg);
throw new Error("Oh, oh...");
return "mundo";
})
.then(function(msg) {
console.log(msg);
})
.fail(function(err) {
console.log(err);
return "MAL!"
})
promise.resolve(42);
Promesas: walled garden
var fakePromise = require("./fakePromise");
var promise = fakePromise.gimmePromise();
promise.then(function() {
return "hola";
})
.then(function(msg) {
console.log(msg);
throw new Error("Oh, oh...");
return "mundo";
})
.then(function(msg) {
console.log(msg);
})
.fail(function(err) {
console.log(err);
return "MAL!"
})
promise.resolve(42);
Promesas: walled garden
var fakePromise = require("./fakePromise");
var promise = fakePromise.gimmePromise();
promise.then(function() {
return "hola";
})
.then(function(msg) {
console.log(msg);
throw new Error("Oh, oh...");
return "mundo";
})
.then(function(msg) {
console.log(msg);
})
.fail(function(err) {
console.log(err);
return "MAL!"
})
promise.resolve(42);
Promesas: walled garden
var fakePromise = require("./fakePromise");
var promise = fakePromise.gimmePromise();
promise.then(function() {
return "hola";
})
.then(function(msg) {
console.log(msg);
throw new Error("Oh, oh...");
return "mundo";
})
.then(function(msg) {
console.log(msg);
})
.fail(function(err) {
console.log(err);
return "MAL!"
})
promise.resolve(42);
Promesas: walled garden
var fakePromise = require("./fakePromise");
var promise = fakePromise.gimmePromise();
promise.then(function(msg) {
console.log(msg);
throw new Error("Oh, oh...");
return "mundo";
})
.then(function(msg) {
console.log(msg);
return "OK!";
})
.fail(function(err) {
console.log(err);
return "MAL!";
})
.then(function(msg) {
console.log(msg);
})
promise.resolve("hola");
Promesas: walled garden
Propagación de promesas
var fakePromise = require("./fakePromise");
var promise = fakePromise.gimmePromise(),
promise2 = fakePromise.gimmePromise();
promise.then(function(val) {
console.log("promise:", val);
promise2.then(function(val) {
console.log("promise2:", val);
});
});
promise.resolve(42);
setTimeout(promise2.resolve.bind(promise2, 12), 2000);
Promesas: walled garden
Propagación de promesas
var fakePromise = require("./fakePromise");
var promise = fakePromise.gimmePromise(),
promise2 = fakePromise.gimmePromise();
promise.then(function(val) {
console.log("promise:", val);
promise2.then(function(val) {
console.log("promise2:", val);
});
});
promise.resolve(42);
setTimeout(promise2.resolve.bind(promise2, 12), 2000);
Promesas: walled garden
Propagación de promesas
var fakePromise = require("./fakePromise");
var promise = fakePromise.gimmePromise(),
promise2 = fakePromise.gimmePromise();
promise.then(function(val) {
console.log("promise:", val);
return promise2
})
.then(function(val) {
console.log("promise2:", val);
});
promise.resolve(42);
setTimeout(promise2.resolve.bind(promise2, 12), 2000);
Promesas: walled garden
Propagación de promesas
var fakePromise = require("./fakePromise");
var promise = fakePromise.gimmePromise(),
promise2 = fakePromise.gimmePromise();
promise.then(function(val) {
console.log("promise:", val);
return promise2
})
.then(function(val) {
console.log("promise2:", val);
});
promise.resolve(42);
setTimeout(promise2.resolve.bind(promise2, 12), 2000);
Promesas: walled garden
¿Y al revés?
var fakePromise = require("./fakePromise");
var promise = fakePromise.gimmePromise(),
promise2 = fakePromise.gimmePromise();
promise.then(function(val) {
console.log("promise:", val);
return promise2
})
.then(function(val) {
console.log("promise2:", val);
});
setTimeout(promise.resolve.bind(promise, 42), 2000);
promise2.resolve(12);
Diferidos
En el mundo real, las cosas son un poco distintas
Las “promesas” están divididas en dos objetos:

• La promesa en sí
- Interfaz limitada a la construcción de flujos
- .then, .fail y algún método más

• Su diferido
- Interfaz limitada a controlar el estado
- .reject y .resolve
Diferidos
var Q = require("q");
var defer = Q.defer(),
promise = defer.promise;
// flujo
promise.then(function(val) {
console.log("val:", val);
}, function(err) {
console.log("Error!");
});
// estado
defer.resolve(42);
Diferidos
var Q = require("q");
var defer = Q.defer(),
promise = defer.promise;
// flujo
promise.then(function(val) {
console.log("val:", val);
}, function(err) {
console.log("Error!");
});
// estado
defer.resolve(42);
Diferidos
Cumplen dos roles diferentes:

• Diferido lo controla el gestor del recurso/proceso que
se está modelando

• Promesa es el interfaz para que el consumidor del
recurso pueda construir el flujo que necesita
Promesas
Un ejemplo realista: readFilePromise(file)

• Implementa la función
• Basándote en fs.readFile()
• Devuelve una promesa
• Se resuelve con el contenido del fichero
• O se rechaza con el error
Promesas
function readFilePromise(filePath) {
// ???
}
readFilePromise("./hola.txt").then(function(contenido) {
console.log("contenido:", contenido.toString());
}, function(err) {
console.log("ERROR!", err);
});
Promesas
var fs = require("fs"),
Q = require("q");
function readFilePromise(filePath) {
var defer = Q.defer();
fs.readFile(filePath, function(err, data) {
err ? defer.reject(err) : defer.resolve(data);
})
return defer.promise;
}
readFilePromise("./hola.txt").then(function(contenido) {
console.log("contenido:", contenido.toString());
}, function(err) {
console.log("ERROR!", err);
});
Promesas
var fs = require("fs"),
Q = require("q");
Gestor del recurso
function readFilePromise(filePath) {
var defer = Q.defer();
fs.readFile(filePath, function(err, data) {
err ? defer.reject(err) : defer.resolve(data);
})
return defer.promise;
}

Consumidor del recurso

readFilePromise("./hola.txt").then(function(contenido) {
console.log("contenido:", contenido.toString());
}, function(err) {
console.log("ERROR!", err);
});
Promesas: paréntesis
Intenta modificar el servidor de ficheros estáticos
para que utilice promesas:

• fileExistsPromise
• readFilePromise
Promesas: combinaciones
Q es una librería muy potente

• Trae un montón de funcionalidad
• Muy recomendable leer la documentación
• Nosotros vamos a ver dos cosas esenciales:
- Combinación de promesas en paralelo
- Adaptadores de llamadas con formato Node.js
Q.all([promise, promise, ...])
Crea una promesa que representa al conjunto

• La nueva promesa se resuelve cuando todas las del
conjunto se hayan resuelto

• Su valor es un array con los valores de las promesas
• Si una del conjunto es rechazada, la nueva promesa
también es rechazada
Q.all([promise, promise, ...])
var Q = require("q");
var def1 = Q.defer(),
prom1 = def1.promise,
def2 = Q.defer(),
prom2 = def2.promise;
Q.all([prom1, prom2]).then(function(v) {
console.log("Todas resueltas!");
console.log(v); // [42, 13]
})
def1.resolve(42);
setTimeout(def2.resolve.bind(def2, 13), 1000);
Q.all([promise, promise, ...])
Q.spread([v1, v2], callback)
“Reparte” valores de un array en parámetros
var Q = require("q");
Q.spread([1,2,3,4], function(a, b, c, d) {
console.log(a, b, c, d); // 1 2 3 4
})
Q.spread([v1, v2], callback)
Invoca automáticamente a Q.all(...)!
var Q = require("q");
var def1 = Q.defer(), def2 = Q.defer(), def3 = Q.defer(),
pro1 = def1.promise, pro2 = def2.promise, pro3 = def3.promise;
pro1.then(function(v1) {
console.log(v1);
return [pro2, pro3];
})
.spread(function(v2, v3) {
console.log(v2, v3);
});
def1.resolve(42);
setTimeout(def2.resolve.bind(def2, 13), 1000);
setTimeout(def3.resolve.bind(def3, 71), 200);
Q.spread([v1, v2], callback)
Invoca automáticamente a Q.all(...)!
var Q = require("q");
var def1 = Q.defer(), def2 = Q.defer(), def3 = Q.defer(),
pro1 = def1.promise, pro2 = def2.promise, pro3 = def3.promise;
pro1.then(function(v1) {
console.log(v1);
return [pro2, pro3];
})
.spread(function(v2, v3) {
console.log(v2, v3);
});
def1.resolve(42);
setTimeout(def2.resolve.bind(def2, 13), 1000);
setTimeout(def3.resolve.bind(def3, 71), 200);
Q.ninvoke(ctx, method, arg, [arg])
Adaptador para convertir llamadas Node.js en
promesas
var Q = require("q"),
fs = require("fs");
Q.ninvoke(fs, "readFile", "./hola.txt")
.then(function(data) {
console.log("Contenido: ", data.toString());
})
.fail(function(err) {
console.log("Oops!", err);
});
Q(promiseOrValue)
Homogeneizar valores:

• Si es una promesa, se queda tal cual
• Si es un valor, se convierte en una promesa que se
resuelve a ese valor
Q(42).then(function(v) {
console.log(v); // 42
})
Q(promise).then(function(v) {
console.log(v); // resolución de promise
})
Promesas: gotcha
¿Qué pasa aquí?
var d = Q.defer(), promise = d.promise;
promise.then(function(v) {
console.log(v);
throw new Error("Vaya por Dios!");
})
.then(function() {
console.log("Hola?");
});
d.resolve(42);
promise.done()
Finaliza el flujo, levantando los errores que no se
hayan manejado
var d = Q.defer(), promise = d.promise;
promise.then(function(v) {
console.log(v);
throw new Error("Vaya por Dios!");
})
.then(function() {
console.log("Hola?");
})
.done();
d.resolve(42);
promise.done()
La regla es:

• Si vas a devolver la promesa, déjala abierta
• Si eres el consumidor final de la promesa, asegúrate de
cerrarla con .done()
¡A teclear!
Vamos el primer ejercicio complicadillo: un servidor
de ficheros versionado

• La herramienta que hemos estado utilizando para
compartir código

• Con promesas
• (Si alguien se atreve, que lo intente hacer sin
promesas...)
Necesitas saber...
Manejar rutas: require(“path”)

• path.resolve(base,

ruta): ruta relativa a ruta

absoluta (partiendo de base)

• path.relative(base,
relativa (desde base)

ruta): ruta absoluta a ruta
Necesitas saber...
fs.readdir(ruta): Listar ficheros de un directorio

• Devuelve un array de strings con los nombres de los
ficheros

• No es recursivo
• No hay manera de saber si una entrada es un fichero o
un directorio
var fs = require("fs"),
Q = require("q");
Q.ninvoke(fs, "readdir", ".").then(function(list) {
console.log(list);
})
Necesitas saber...
fs.stat(ruta): Info sobre un fichero/directorio

• stats.isDirectory(): true si es un directorio
• stats.mtime: Date de la última modificación

var fs = require("fs"),
Q = require("q");
Q.ninvoke(fs, "stat", ".").then(function(stats) {
console.log("es dir?", stats.isDirectory());
console.log("última modificación:", stats.mtime);
})
Primer paso: listado recursivo
Escribe una función listAllFiles(ruta) que:

• Devuelva una promesa
• La promesa se resuelva con un listado recursivo de
todos los ficheros que hay dentro del directorio

• Para cada fichero, genere un objeto del tipo
{path: “/ruta/absoluta.txt”, stats: statsDelFichero}

listAllFiles(".").then(function(list) {
console.log(list);
})
.done()
Primer paso: listado recursivo
Segundo paso: listener
Función somethingChanged(ruta):

• Devuelve true si algún fichero ha sido modificado desde
la última vez que se invocó

• Impleméntalo utilizando stats.mtime como referencia
Utilizando esa función, escribe un “demonio” que
monitorize un directorio y escriba un mensaje por
consola cada vez que hay cambios
Tercer paso: volcado a memoria
Función readAllFiles(ruta):

• Completa el resultado de listAllFiles() añadiendo
una tercera propiedad “contents” con los contenidos
del fichero

Haz que el demonio lea todos los ficheros si detecta
algún cambio y guarda el resultado en posiciones
consecutivas de un array. Este va a ser nuestro
control de versiones.
Cuarto paso: sirve los datos
El interfaz web tiene las siguientes rutas:

• /: listado de versiones
• /list?version=<n>: listado de ficheros de la versión n
• /ruta/al/fichero?version=<n>: busca el fichero con
la ruta correspondiente en la versión n y lo sirve

• la versión “latest” siempre apunta a la versión más
reciente
Virguerías opcionanes
Escribe un módulo simpleRoute de modo que
podamos definir rutas así:
var routes = require("./simpleRoute");
routes.get("/", function(req, res) {
})
routes.get("/list", function(req, res) {
})
routes.default(function(req, res) {
})
Virguerías opcionales
Además, simpleRoute modifica el parámetro
req.url y lo sustituye por la url parseada

var routes = require("./simpleRoute");
routes.get("/list", function(req, res) {
console.log(req.url.pathname);
console.log(req.url.query.version);
})
Express
¿Qué es express?
Un framework web para Node.js

• Estrictamente web (microframework)
• Sencillo y flexible
• Muy popular
• Se adapta muy bien a la filosofía de Node
• Similar a Sinatra, Sylex, Flask, Spark, ...
¿Qué es express?
Express nos va ayudar con...

• Rutas
• Parámetros
• Formularios y subida de ficheros
• Cookies
• Sesiones
• Templates
¿Qué es express?
Express NO nos va ayudar con...

• Base de datos / ORM
• Autenticación de usuarios
• Seguridad
• Migraciones
• Deployment
• Organización del código
¿Qué es express?
Más concretamente, express...

• Construye sobre http
• Procesando la petición por un stack de middleware que
se encarga de decorar las peticiones

- Asocia rutas a manejadores
- Decorar los objetos req y res (parseo de parámetros,
multipart, etc,...)

- Rendear templates

• Nosotros escogemos qué middlewares queremos usar,
y en qué orden
¿Qué es express?
var express = require("express");
var app = express();
// configuración + rutas
app.listen(3000);
¿Qué es express?
Equivalente a:
var express = require("express"),
http = require("http");
var app = express();
// configuración + rutas
http.createServer(app).listen(3000);
¿Qué es express?
Equivalente a:
var express = require("express"),
http = require("http");
var app = express();
// configuración + rutas
http.createServer(app).listen(3000);
¿Qué es express?
var app = require("express")();
app.get("/", function(req, res) {
res.end("Hola desde express!");
});
app.listen(3000);
¿Qué es express?
Verbo

Ruta

Manejador

var app = require("express")();
app.get("/", function(req, res) {
res.end("Hola desde express!");
});
app.listen(3000);
¿Qué es express?
Objeto

Stream

var app = require("express")();
app.get("/", function(req, res) {
res.end("Hola desde express!");
});
app.listen(3000);
¿Qué es express?
¿Qué nos aporta express, exactamente?

• Depende de los middlewares que usemos!
• Algunas cosas vienen por defecto
Request
req.params: parámetros de la ruta

app.get("/user/:id", function(req, res) {
req.params.id;
});
Request
req.query: la querystring, parseada
app.get("/search", function(req, res) {
// GET /search?text=nodejs+express
req.query.text;
});
Request
• req.ip: IP del cliente conectado
• req.host: Hostname del servidor
• req.xhr: ¿Es ajax?
• req.acceptedLanguages: Array de locales
• req.host: Hostname del servidor
• Mas info en http://express.js.com/api.html
Response
res.cookie(nombre, valor, [opciones])

• Modifica la cookie “nombre” con el valor “valor”

res.cookie("visitas", "1", {domain: ".ejemplo.com"});
Response
res.redirect([status], url)

• Redirige a url
• El código de estado es opcional (302 por defecto)

res.redirect(301, "http://www.google.com");
Response
res.send([status], body)

• Envía una respuesta (escribe en el buffer)
• Lo más adecuado para respuestas sencillas
- no streaming

• Automatiza ciertas cabeceras
- Content-Type, Content-Length

• Convierte objetos a JSON
res.send(500, {msg: "Oh, oh..."});
Response
Muchos otros métodos auxiliares:

• Cabeceras
• Envío de ficheros
• JSONP
• Content-Type
• ¡Lee la documentación!
http://expressjs.com/api.html
Application
app.configure([entorno], callback)

• Configurar la aplicación
• Opcionalmente: configuración para un entorno
- “development”
- “testing”
- “production”
- etc
app.configure('development', function(){
app.set('db uri', 'localhost/dev');
})
Application
¿Qué significa “configurar la aplicación”?

• Crear algunas propiedades globales
• Especificar el stack de middleware
Application
app.set(prop, value) / app.get(prop)

• Escribe/consulta valores globales en app
• Básicamente inútil...
• Excepto para cambiar alguna configuración más
avanzada
app.set('title', 'Redradix');
app.get('title');
Middleware
Middleware son módulos “plug and play” que se
pueden apilar arbitrariamente en cualquier orden
y proveen cierta funcionalidad

• Filtros: procesan tráfico entrate/saliente, pero no
responden a ninguna request. (ejemplo: bodyParser)

• Proveedores: ofrecen respuestas automáticas a algún
tipo de petición (ejemplo: static provider)
Middleware
app.configure(function() {
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser('your secret here'));
app.use(express.session());
app.use(app.router);
app.use(express.static(__dirname +
});

'/public'));
Middleware
app.configure(function() {
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser('your secret here'));
app.use(express.session());
app.use(app.router);
app.use(express.static(__dirname +
});

'/public'));
Middleware
app.configure(function() {
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser('your secret here'));
app.use(express.session());
app.use(app.router);
app.use(express.static(__dirname +
});

'/public'));
Middleware
Express trae unos cuantos preinstalados

• http://www.senchalabs.org/connect/
Una lista de módulos de terceros

• https://github.com/senchalabs/connect/wiki
express.favicon(ruta)
Sirve el favicon de la aplicación

• Debe ser el primero
• Para evitar capas inncesarias
• log
• parseo
• cookies
• etc...
express.logger([opciones])
Registro de actividad

• Muchas opciones...
http://www.senchalabs.org/connect/logger.html

• Se suele poner debajo de express.favicon()
express.cookieParser([secret])
Parsea las cookies de la petición

• Opcional: firmar cookies con secret
• Crea los objetos req.cookies y req.signedCookies
app.configure(function(){
app.use(express.cookieParser('secreto'));
})
app.get("/", function(req, res) {
console.log(req.cookies);
console.log(req.signedCookies);
res.send(200);
})
express.bodyParser()
Parsea el cuerpo de las peticiones POST

• Decodifica
- application/json
- application/x-www-form-urlencoded
- multipart/form-data

• Crea el objeto req.body con los parámetros POST
• Crea el objeto req.files con los ficheros que se han
subido desde un formulario
express.cookieSession([opciones])
Inicializa y parsea los datos de sesión del usuario

• Crea el objeto req.session
• Utilizando cookies como almacenamiento
• Opciones:
- secret: firma de segurdad para la cookie
- maxAge: duración, en ms (default: sin caducidad)
- path: ruta para la que es válida la cookie (default: /)
- httpOnly: protegida del cliente (default: true)
express.cookieSession([opciones])
var express = require("express"),
app = express();
app.configure(function(){
app.use(express.cookieParser('secreto'));
app.use(express.cookieSession());
})
app.get("/", function(req, res) {
req.session.visitas || (req.session.visitas = 0);
var n = req.session.visitas++;
res.send("Me has visitado: " + n + " veces!");
})
app.listen(3000);
express.static(dir)
Sirve los ficheros estáticos dentro de dir

• ¡Muy útil! Se pone cerca del final
• Cachea los ficheros
• La variable global __dirname contiene el directorio
donde reside el script en ejecución
app.router
El enrutado de la aplicación

• Sirve para específicar exáctamente en qué momento
quieres que se procesen las rutas de tu app
Middleware
app.configure(function() {
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser('your secret here'));
app.use(express.cookieSession());
app.use(app.router);
app.use(express.static(__dirname +
});

'/public'));
Templates
Express tiene un mecanismo para rendear templates

• Agnóstico
• Modular
• Simple
• NO trae ningún motor de templates por defecto
Templates
res.render(view, [locals], callback)

• view: ruta del template
• locals: valores a interpolar
• callback: function(err, html)

{ ... }
Templates
Tenemos muchos motores de templates para elegir!
https://npmjs.org/browse/keyword/template

• Haml
• Hogan/Mustache
• Twig/Swig
• Ejs
• Jinja
• Jade
....
Ejs
<h1><%= title %></h1>
<ul>
<% for(var i=0; i<supplies.length; i++) { %>
<li>
<a href='supplies/<%= supplies[i] %>'>
<%= supplies[i] %>
</a>
</li>
<% } %>
</ul>
Jade
Templates
Algunas propuestas originales/innovadoras:
CoffeeKup: http://coffeekup.org/
Weld: https://github.com/tmpvar/weld
Domo: http://domo-js.com/
Templates
Para utilizarlos desde express:
var express = require("express"),
app = express();
app.configure(function() {
app.engine("jade", require("jade").__express);
app.set("views", "./views");
app.set("view engine", "jade");
})
app.get("/", function(req, res) {
res.render("welcome", {user: "Pepito"});
})
app.listen(3000)
Templates
Para utilizarlos desde express:
var express = require("express"),
app = express();
app.configure(function() {
app.engine("jade", require("jade").__express);
app.set("views", "./views");
app.set("view engine", "jade");
})
app.get("/", function(req, res) {
res.render("welcome", {user: "Pepito"});
})
app.listen(3000)
Templates
Para utilizarlos desde express:
var express = require("express"),
app = express();
app.configure(function() {
app.engine("jade", require("jade").__express);
app.set("views", "./views");
app.set("view engine", "jade");
})
app.get("/", function(req, res) {
res.render("welcome", {user: "Pepito"});
})
app.listen(3000)
Templates
Para utilizarlos desde express:
var express = require("express"),
app = express();
app.configure(function() {
app.engine("jade", require("jade").__express);
app.set("views", "./views");
app.set("view engine", "jade");
})
app.get("/", function(req, res) {
res.render("welcome", {user: "Pepito"});
})
app.listen(3000)
Templates
Para utilizarlos desde express:
var express = require("express"),
app = express();
app.configure(function() {
app.engine("jade", require("jade").__express);
app.set("views", "./views");
app.set("view engine", "jade");
})
app.get("/", function(req, res) {
res.render("welcome", {user: "Pepito"});
})
app.listen(3000)
Templates
Donde /views/welcome.jade sería algo así:
doctype 5
html(lang="es")
body
h1 Bienvenido, #{user}
Templates
Express es bastante listo (buenos defaults)

var express = require("express"),
app = express();
app.get("/", function(req, res) {
res.render("welcome.jade", {user: "Pepito"});
})
app.listen(3000)
Blog en 15 minutos
Vamos a poner en práctica lo que hemos visto

• Vamos a hacer un “blog”, simplificado
• Sin BBDD
• Tiene 7 rutas (4 pantallas)
- GET /posts
- GET /posts/new
- POST /posts
- GET /posts/:id
- GET /posts/edit
- PUT /posts/:id
- DEL /posts/:id
Blog en 15 minutos
¿Sin base de datos?

• Guarda los datos en memoria, en un array
• Cuidado si usas nodemon, porque al guardar se resetea
el servidor y se pierde el contenido del array
Un truco: app.param(...)
Mapear parámetros de url
app.param("postid", function(req, res, next, postId) {
req.post = posts.find(postId);
if (err || !req.post) {
next(err || new Error("Post no encontrado ("+postId+")"));
} else {
next();
}
});
app.get("/posts/:postid", function(req, res) {
console.log(req.post);
});
Más Middleware
Escribir middleware para express es muy sencillo:

• Una función que recibe tres parámetros:
- req
- res
- next

• Al terminar su tarea, tiene que invocar a next()
- Sin parámetro: se invoca al siguiente middleware del stack
- Con parámetro: se cambia la ruta a lo que se pase como
parámetro
Más Middleware
Por ejemplo, un logger simple:
app.configure(function(){
app.use(function(req, res, next) {
console.log(" * %s: %s %s",
req.connection.remoteAddress,
req.method,
req.url);
next();
});
})
Más Middleware
Haz un logger que muestre:

• Hora de la petición
• IP
• Método
• Ruta
• Tiempo de respuesta de la petición
Pero solo si el tiempo de respuesta está muy por
encima de la media!
Más Middleware
Haz un módulo de “mensajes flash”

• Mensajes que solo están disponibles para la siguiente
request

• Muy útiles para informar de ciertos sucesos
- “Post creado con éxito”
- “Email enviado”
- Errores
- etc...
Más Middleware
app.post("/posts", function(req, res) {
var post = createPost(req.body);
if (post) {
req.flash.message("Post creado con éxito!");
} else {
req.flash.error("No se ha podido crear...");
}
res.redirect("/posts/" + post.id);
})
app.get("/posts/:postid", function(req, res) {
console.log(req.flash.message());
console.log(req.flash.error());
})
Más Middleware
app.post("/posts", function(req, res) {
var post = createPost(req.body);
if (post) {
req.flash.message("Post creado con éxito!");
} else {
req.flash.error("No se ha podido crear...");
}
res.redirect("/posts/" + post.id);
})
app.get("/posts/:postid", function(req, res) {
console.log(req.flash.message());
console.log(req.flash.error());
})
Más Middleware
app.post("/posts", function(req, res) {
var post = createPost(req.body);
if (post) {
req.flash.message("Post creado con éxito!");
} else {
req.flash.error("No se ha podido crear...");
}
res.redirect("/posts/" + post.id);
})
app.get("/posts/:postid", function(req, res) {
console.log(req.flash.message());
console.log(req.flash.error());
})
Más Middleware: Errores
Un caso especial de middleware: una función que
reciba un parámetro más
var express = require("express"),
app = express();
app.get("/", function(req, res) {
throw new Error("??")
})
app.use(function(err, req, res, next) {
console.log("* SOCORRO! ALGO VA MAL! ", err);
res.send(500)
})
app.listen(3000)
Más Middleware: Errores
Más correcto así:
var express = require("express"),
app = express();
app.get("/", function(req, res, next) {
next(new Error("esta app no hace nada"));
})
app.use(function(err, req, res, next) {
console.log("* SOCORRO! ALGO VA MAL! ", err);
res.send(500)
})
app.listen(3000)
Más Middleware: Errores
Escribe un manejador de errores para el blog

• Página 404 (página no encontrada o ruta no existe)
• Página 500 (mostrar en caso de error/excepción)
• Registra la fecha, la ruta y el mensaje de error en
errors.log
Más Middleware
Dos maneras de activar middlewares

• Globalmente (app.use), activos para toda la app
• Locales para ciertas rutas
Más Middleware
var express = require("express"),
app = express()
function logThis(req, res, next) {
console.log(" -> ", req.url);
next();
}
app.get("/", logThis, function(req, res) {
res.send("Ok!");
})
app.listen(3000);
Más Middleware
Afinar la funcionalidad de cada ruta

• Pre-procesado de URL (parámetros, formato)
• Seguridad
• Caché
• Métricas
Más Middleware
Escribe un módulo para cachear la respuesta de una
ruta durante X ms
Duración en ms

app.get("/", new StaticCache(5*1000), function(req, res) {
res.send("Envío mi respuesta..." + new Date());
})
Más Middleware
Una variación:

• Cachea a un fichero
• Sustituye res.write por el stream del fichero
• Cuando se dispare “finish”, sirve lo que has
almacenado

• Puedes añadirlo al ejercicio del Blog para cachear las
páginas de detalle de un post
SimpleAuth
Módulo para autenticar usuarios simpleAuth.js

• Sencillo y flexible
• Independiente de la BBDD
• Utilizando las sesiones de express
• Middleware de ruta
SimpleAuth
El módulo provee 3 middlewares:

• createSession: comprueba los credenciales de usuario
y crea una nueva sesión si son correctos

• requiresSession: protege una ruta y solo deja pasar a
usuarios autenticados

• destroySession: desloguea a un usuario
SimpleAuth
var auth = require("./simpleAuth");
app.post("/login", auth.createSession({ redirect: "/secret" }))
app.get("/secret", auth.requiresSession, function(req, res) {
res.end("Hola, " + req.user.email);
})
app.get("/logout", auth.destroySession, function(req, res) {
res.end("Te has deslogueado");
});
SimpleAuth
El usuario ha de definir una estrategia:
serializeUser: ¿Cómo serializar un usuario?
deserializeUser: ¿Cómo des-serializar un usuario?
checkCredentials: ¿Son correctos los credenciales?
SimpleAuth
var auth = require("./simpleAuth")
var users = [{email: "admin@asdf.com", pass: "asdf", id: 0}];
auth.setStrategy({
serializeUser: function(user) {
return user.id;
},
deserializeUser: function(userId, cb) {
cb(users[userId]);
},
checkCredentials: function(email, pass, cb) {
var admin = users[0];
if (email === admin.email && pass === admin.pass) {
cb(null, admin);
} else {
cb(null, false);
}
}
});
SimpleAuth
auth.createSession(config)

• config.username: nombre del campo del formulario
• config.password: nombre del campo del formulario
• config.redirect: URL en caso de éxito
• config.failRedirect: URL en caso de error
- default: /login

• Devuelve: una función para usar como middleware
SimpleAuth
auth.createSession(config)

• Genera una función que...
1. Extrae username y password según config de req.body
2. Llama a strategy.checkCredentials con username y
password

3. Si son credenciales correctos:
3.1. Crea una entrada en la sessión o cookie con el resultado de llamar a
strategy.serializeUser(usuario), donde usuario es lo que
devuelve strategy.checkCredentials
3.2. Redirige a la URL de éxito

4. Si no son correctos:
4.1. Redirige a la URL de error
SimpleAuth
auth.requiresSession
1. Se asegura de que exista la entrada adecuada en la
sesión o cookie
2. Llama a strategy.deserializeUser con el valor
3. Guarda el usuario des-serializado en req.user
4. En caso de error, borra la sesión o cookie
SimpleAuth
auth.destroySession
1. Borra la entrada adecuada en la sesión o cookie
SimpleAuth
auth.setStrategy(strategy)
1. Configura la estretagia:

- strategy.serializeUser
- strategy.deserializeUser
- strategy.checkCredentials
- strategy.loginRoute
SimpleAuth
Protege la edición del blog con simpleAuth

• Utilizando un array de usuarios escrito directamente en
el código

• Página de login + link logout + solo los usuarios
logueados pueden crear o editar posts

• Si te sobra tiempo, haz que los usuarios se puedan
registrar
Redis
¿Qué es Redis?
Una base de datos NoSQL...

• Clave/valor
• Extremadamente rápida
• Persistencia y transacciones
• Estructuras de datos
- Listas
- Conjuntos
- Hashes

• Pub/sub
¿Qué es Redis?
Un servidor de estructuras de datos

• Sin tablas, ni queries ni JOINs
• Una manera de pensar muy diferente
• Muchas queries muy rápidas
• Muy fácil de aprender, muy fácil de usar mal
¿Qué es Redis?
Redis es una opción estupenda para...

• Datos de acceso inmediato
- Sesiones
- Cachés

• Escrituras muy rápidas
- Logs
- Conteos y estadísticas

• Canal de comunicación pub/sub
- Comunicación entre procesos (workers, big data)
- Chats :)
¿Qué NO es Redis?
Redis es una opción terrible para...

• Relaciones de datos complejas
• Búsquedas
¿Para qué sirve?
Mi consejo:

• NO bases tu app en redis
• A no ser que sea muy simple o sepas muy bien lo que
haces

• Utiliza Redis como complemento a tu BBDD
- Cachés
- Estadísticas
- Workers
- Sesiones
- Registros
- Colas
La idea general
Redis es un gran Hash de claves

• No hay concepto de tabla/colección
• El valor fundamental es la cadena (string)
• Pero una clave puede contener un valor más complejo
- Hashes (string -> string)
- Listas (de strings)
- Sets (de strings)
- Sets ordenados (de strings)
Requisitos
Necesitas:

• Tener instalado y arrancado redis
• Tener a mano la documentación
➡ http://redis.io/commands

•

npm install hiredis redis

• Como alternativa:
➡ c9.io

-

nada-nix install redis
npm install hiredis redis
redis-server --port 16379 --bind $IP
Primeros pasos
Primero, necesitas conectarte al servidor
var redis = require("redis");
var client = redis.createClient();
// c9.io:
// var client = redis.createClient(16379, process.env.IP);
Primeros pasos
Ahora puedes mandar comandos a Redis
•
•

client.nombreDelComando(arg, callback)

•

callback: function(err, value) { }

client.nombreDelComando(arg1, arg2, callback)
SET / GET
Guardar y recuperar un valor
var redis = require("redis"),
client = redis.createClient();
client.set("miClave", "miValor", function(err, val) {
console.log(arguments);
client.get("miClave", function(err, value) {
console.log("valor: ", value);
});
})
SET / GET
Guardar y recuperar un valor
var redis = require("redis"),
client = redis.createClient();
client.set("miClave", "miValor", function(err, val) {
console.log(arguments);
client.get("miClave", redis.print);
})
SET / GET
Guardar y recuperar un valor
var redis = require("redis"),
client = redis.createClient();
client.set("miClave", "miValor", function(err, val) {
console.log(arguments);
client.get("miClave", function(err, value) {
console.log("valor: ", value);
});
})
SET / GET
Guardar y recuperar un valor
var redis = require("redis"),
client = redis.createClient();
client.set("miClave", "miValor", function(err, val) {
console.log(arguments);
client.get("miClave", function(err, value) {
console.log("valor: ", value);
});
})
SET / GET
Guardar y recuperar un valor
var redis = require("redis"),
Q = require("q"),
client = redis.createClient();
Q.ninvoke(client, "set", "miClave", "miValor")
.then(function() {
return Q.ninvoke(client, "get", "miClave");
})
.then(function(value) {
console.log("Valor: ", value);
})
.done();
SET / GET
Guardar y recuperar un valor
Un truco: redis-cli monitor
Para ver qué está pasando en Redis
Redis = Strings!!
Cuidado con los valores. Han de ser siempre strings.
var redis = require("redis"),
Q = require("q"),
client = redis.createClient();
Q.ninvoke(client, "set", "miClave", {un: "objeto"})
.then(function() {
return Q.ninvoke(client, "get", "miClave");
})
.then(function(value) {
console.log("Valor: ", value); // Valor: [object Object]
})
.done();
Redis = Strings!!
var obj = {propiedad: "valor"};
Q.ninvoke(client, "set", "miClave", JSON.stringify(obj))
.then(function() {
return Q.ninvoke(client, "get", "miClave");
})
.then(function(value) {
console.log("Valor: ", value);
// Valor: {"propiedad": "valor"}
})
.done();
Redis = Strings!!
var serializable = {
toString: function() { return JSON.stringify(this); }
};
var obj = Object.create(serializable, {
propiedad: {
enumerable: true,
value: "valor"
}
});
Q.ninvoke(client, "set", "miClave", obj)
.then(function() {
return Q.ninvoke(client, "get", "miClave");
})
.then(function(value) {
console.log("Valor: ", value); // Valor: {"propiedad": "valor"}
})
.done();
Redis = Strings!!
var serializable = {
toString: function() { return JSON.stringify(this); }
};
var obj = Object.create(serializable);
obj.propiedad = "valor";
Q.ninvoke(client, "set", "miClave", obj)
.then(function() {
return Q.ninvoke(client, "get", "miClave");
})
.then(function(value) {
console.log("Valor: ", value);
// Valor: {"propiedad": "valor"}
})
.done();
Redis = Strings!!
Una solución más radical:
Object.prototype.toString = function() {
return JSON.stringify(this);
};
var obj = {propiedad: "valor"};
Q.ninvoke(client, "set", "miClave", obj)
.then(function() {
return Q.ninvoke(client, "get", "miClave");
})
.then(function(value) {
console.log("Valor: ", value);
// Valor: {"propiedad": "valor"}
})
.done();
DEL / EXISTS / TYPE / RENAME
Operaciones con claves

• DEL: borrar una clave
• EXSITS: comprobar si una clave existe
• TYPE: el tipo de valor almacenado en una clave
• RENAME: cambiar el nombre de la calve
DEL / EXISTS / TYPE / RENAME
var redis = require("redis"),
Q = require("q"),
client = redis.createClient();
Q.ninvoke(client, "exists", "MiClave")
.then(function(exists) {
console.log( exists? "Existe!" : "No existe..." );
return Q.ninvoke(client, "set", "MiClave", "MiValor");
})
.then(function() {
return Q.ninvoke(client, "rename", "MiClave", "MyKey");
})
.then(function() {
return Q.ninvoke(client, "type", "MyKey");
})
.then(function(type) {
console.log("MyKey es de tipo", type);
})
.done();
Operaciones con cadenas
• APPEND: añade el valor a la cadena
• DECR/INCR: Decrementa/incrementa el valor en 1
• DECRBY/INCRBY: Dec/inc el valor en N
• GETSET: Modifica el valor y devuelve el viejo
• STRLEN: Longitud del valor
Operaciones con cadenas
var op = Q.ninvoke.bind(Q, client);
op("set", "miClave", 1)
.then(function() {
return op("incrby", "miClave", 10);
}).then(function() {
return op("decr", "miClave");
}).then(function() {
return op("getset", "miClave", "fin");
}).then(function(valor) {
console.log("VALOR: ", valor);
return op("strlen", "miClave")
}).then(function(len) {
console.log("Len: ", len);
})
.done();
Operaciones múltiples
• MGET: Trae el valor de varias claves
• MSET: Modifica el valor de varias claves
var op = Q.ninvoke.bind(Q, client);
op("mset", "miClave", 1, "otraClave", 2)
.then(function() {
return op("mget", "miClave", "otraClave");
})
.then(function(values) {
console.log(values[0], ",", values[1]);
})
.done()
Listas
• LPUSH/RPUSH key value [value ...]
• LPOP/RPOP key
• LINDEX key index
• LSET key index value
• LLEN key
• LRANGE key start stop: trae el rango de elementos
• LTRIM key start stop: limita al rango start-stop
• RPOPLPUSH source dest: RPOP sour + LPUSH dest
Listas
var op = Q.ninvoke.bind(Q, client),
makeOp = function() {
var args = arguments;
return function() { return op.apply(null, args); }
};
op("del", "miClave")
.then(makeOp("rpush", "miClave", 1, 2, 3, 4))
.then(makeOp("lrange", "miClave", 0, 2))
.then(function(values) {
console.log(values);
return op("ltrim", "miClave", 0, 1);
})
.then(makeOp("llen", "miClave"))
.then(function(len) {
console.log(len);
})
.done()
A teclear un poco!
Modifica el ejercicio del blog del tema anterior...

• Para que utilice Redis como BD
• Guardar los posts como objetos JSON
• Guarda los usuarios en claves tipo:
- “user:admin@asdf.com”: <JSON del usuario>

• Modifica la estrategia del autenticación
Hashes
• HSET

key field value: Modifica el valor de campo

field del hash en value

• HGET

key field: Consulta el valor de campo field

del hash en value

• HEXISTS key field: Existe el campo field?
• HKEYS/HVALS key: Todos los campos/valores
• HGETALL: Trae el hash entero
• HINCRBY key field n: Incrementa el campo en n
• HMGET/HMSET: Operaciones múltiples
Hashes
op("del", "miClave").then(function() {
return op("hmset", "miClave", "a", 1, "b", 2, "c", 3);
})
.then(function() {
return op("hincrby", "miClave", "c", 100);
})
.then(function() {
return op("hgetall", "miClave");
})
.then(function(hash) {
console.log(hash);
// { a: '1', b: '2', c: '103' }

})
.done()
Conjuntos
• SADD

key member [member ...]: añadir miembros

• SREM

key member [member ...]: quitar miembros

• SCARD

key: cardinal (número de elementos)

• SDIFF key [key ...]
• SINTER key [key ...]
• SUNION key [key ...]
• SISMEMBER
• SMEMBERS

key member: ¿es miembro?

key: todos los miembros
Conjuntos
var op = Q.ninvoke.bind(Q, client),
makeOp = function() {
var args = arguments;
return function() { return op.apply(null, args); }
};
op("sadd", "miConjunto", 1, 1, 2, 3, 5, 8, 13)
.then(makeOp("sadd", "miConjunto2", 1, 3, 5, 7, 9, 11, 13))
.then(makeOp("sinter", "miConjunto", "miConjunto2"))
.then(function(values) {
console.log("Intersección:", values);
return op("sdiff", "miConjunto", "miConjunto2");
})
.then(function(values) {
console.log("Diferencia:", values);
})
.done()
Ejercicio: acotador de URLs
Escribe un acortador de URLs con Redis

• Registro y login de usuarios utilizando simpleAuth
• Redirección automática de urls
• Estadísticas de visita
- Cada IP cuenta una sola vez
- ¿Estadísticas por fecha? ¿Tendencias?
- ¿Geotracking de visitas (freegeoip.net)?
http.get("http://freegeoip.net/json/83.44.23.171", function(res) {
res.on("data", function(data) {
console.log(JSON.parse(data));
});
});
MongoDB
¿Qué es MongoDB?
Una base de datos NoSQL...

• Sin esquema
• Alto rendimiento
• Almacena documentos BSON
• Enfocada en escalabilidad horizontal
• Lenguaje de consultas potente
• Sin transacciones
• Agregado de datos
¿Qué es MongoDB?
Una base de datos de documentos

• No impone forma a los datos
• No necesita migraciones/reestructuración de la BBDD
• Permite estructuras muy complejas
• Herramientas potentes de agregado con JavaScript
- Map-Reduce
- Aggregation Pipeline
¿Qué es MongoDB?
Con algunos extras interesantes...

• Índices geoespaciales
• Búsquedas FTS
• Almacenamiento eficiente de blobs y ficheros
• Sharding automático
• Replicación
¿Qué es MongoDB?
Es una buena alternativa para... ¡muchas cosas!

• Prototipos y aplicaciones simples
• Hacer la transición de front a back
• Aplicaciones con mucha carga de escritura
• Agregado de datos a un nivel medio/alto
• Aplicaciones con datos muy heterogéneos
• Enormes colecciones de datos (sharding)
• Almacenar ficheros (sharding)
¿Qué NO es MongoDB?
No te dejes seducir demasiado:

• Mongo no puede hacer JOINs!
• El lenguaje de consulta menos potente que SQL
• No tiene transacciones!
• La velocidad baja al subir la seguridad (escritura)
• Ten cuidado:
- Es muy fácil empezar con MongoDB
- Si tu app crece mucho... vas a necesitar JOINs
La idea general
Un almacen de documentos

• Básicamente, objetos JSON (BSON)
• Dividido en colecciones
• Consultas basadas en la estructura del documento
• ¡Se integra genial con JavaScript!
Requisitos
Necesitas

• Instalar y arrancar mongod
‣

mongod --dbpath <path> --nojournal

• Tener la documentación a mano
➡ http://docs.mongodb.org/manual/
➡ http://mongodb.github.io/node-mongodb-native/

• npm install
• En c9.io

mongodb

•<comandos para instalar mongodb>
Primeros pasos
Para conectarte al servidor
var MongoClient = require("mongodb").MongoClient
, ObjectID = require("mongodb").ObjectID;
var client = Q.ninvoke(MongoClient,
"connect",
"mongodb://127.0.0.1:27017/dbname");
client.fail(function(e) {
console.log("ERROR conectando a Mongo: ", e)
});
Primeros pasos
La BD está dividida en colecciones. Para abrir una
colección:

var collection = client.then(function(db) {
return db.collection("coleccion");
});
Documento
Un documento es un objeto BSON
{
"_id" : ObjectId("524872a99c50880000000001"),
"email" : "test@asdf.com",
"password" : "asdf1234",
"name" : "Test User",
"date" : 1380479657300,
"token" : "hm6ly43v.0o1or"
}
Documento
Un documento es un objeto BSON
{
"_id" : ObjectId("524872a99c50880000000001"),
"email" : "test@asdf.com",
"password" : "asdf1234",
"name" : "Test User",
"date" : 1380479657300,
"token" : "hm6ly43v.0o1or"
}
Documento
Un documento puede contener arrays y otros
documentos
{
"_id" : ObjectId("5249a2e9b90687d56453b2f3"),
"text" : "Soy un comentario",
"user" : {
"_id" : ObjectId("524872a99c50880000000001"),
"nombre" : "Test User",
"avatar" : "/img/as09a8sd09.jpg"
},
"tags" : [ "test", "prueba" ]
}
Documento
Un documento puede contener arrays y otros
documentos
{
"_id" : ObjectId("5249a2e9b90687d56453b2f3"),
"text" : "Soy un comentario",
"user" : {
"_id" : ObjectId("524872a99c50880000000001"),
"nombre" : "Test User",
"avatar" : "/img/as09a8sd09.jpg"
},
"tags" : [ "test", "prueba" ]
}
Documento
Un documento puede contener arrays y otros
documentos
{
"_id" : ObjectId("5249a2e9b90687d56453b2f3"),
"text" : "Soy un comentario",
"user" : {
"_id" : ObjectId("524872a99c50880000000001"),
"nombre" : "Test User",
"avatar" : "/img/as09a8sd09.jpg"
},
"tags" : [ "test", "prueba" ]
}
Documentos
MongoDB no puede hacer JOINs

• Sin embargo, se pueden empotrar documentos y arrays
• Profundidad y complejidad arbitraria
• El límite: documento < 16MB
• Se suelen favorecer los diseños desnormalizados
✓ Mucho más cómodos
✓ Más eficientes en Mongo (con cuidado)
๏ Redundancia...
๏ Posible inconsistencia
๏ Actualizar los datos a mano cuando cambian
Colecciones
Una colección es una agrupación de documentos

• Puede alojar cualquier documento (no impone
estructura)

• Puede alojar documentos con diferentes formas
• Operaciones de consulta
• Es donde se ponen los índices
Colecciones
Operaciones sobre una colección:

• collection.save: guardar/actualizar un documento
• collection.insert: inserta un documento
• collection.findOne: recuperar un documento
• collection.find: recuperar varios documentos
• collection.remove: borrar uno o varios documentos
• collection.drop: elimina la colección
• collection.rename: cambia de nombre la colección
• collection.count: número de documentos
Colecciones
MongoDB trae un cliente de línea de comandos

• mongo <host>/<dbname>
• Ejecuta JavaScript
• Muy práctico para explorar
Colecciones
Colecciones
Colecciones
Desde Node.js
var MongoClient = require("mongodb").MongoClient
, ObjectID = require("mongodb").ObjectID
, Q = require("q");
Q.ninvoke(MongoClient, "connect", "mongodb://127.0.0.1:27017/dbname")
.then(function(db) {
var micoleccion = db.collection("micoleccion"),
op = Q.ninvoke.bind(Q, micoleccion);
op("insert", {uno: 1, dos: 2})
.then(function() { return op("insert", {tres: 3, cuatro: [4]}); })
.then(function() { return op("findOne", {uno: 1}); })
.then(function(doc) { console.log(doc); })
.done();
});
Consulta
Dos operaciones fundamentales:
•findOne: devuelve un documento
•find: devuelve varios documentos en un cursor
Consulta
Ambos reciben como parámetro las conditiones que
tiene que cumplir el documento:
var micoleccion = db.collection("micoleccion"),
op = Q.ninvoke.bind(Q, micoleccion);
Q.ninvoke(micoleccion, "findOne", {uno: 1})
.then(function(doc) { console.log(doc); });
Q.ninvoke(micoleccion, "findOne", {dos: {$gt: 0}})
.then(function(doc) { console.log(doc); });
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js
Qué es Node.js

Weitere ähnliche Inhalte

Was ist angesagt?

Flutter presentation.pptx
Flutter presentation.pptxFlutter presentation.pptx
Flutter presentation.pptxFalgunSorathiya
 
What is flutter and why should i care?
What is flutter and why should i care?What is flutter and why should i care?
What is flutter and why should i care?Sergi Martínez
 
Hacking google cloud run
Hacking google cloud runHacking google cloud run
Hacking google cloud runAviv Laufer
 
Pune Flutter Presents - Flutter 101
Pune Flutter Presents - Flutter 101Pune Flutter Presents - Flutter 101
Pune Flutter Presents - Flutter 101Arif Amirani
 
Analisis de Usabilidad GOMS: Goals, Objectives, Method, Selection of rules.
Analisis de Usabilidad GOMS: Goals, Objectives, Method, Selection of rules.Analisis de Usabilidad GOMS: Goals, Objectives, Method, Selection of rules.
Analisis de Usabilidad GOMS: Goals, Objectives, Method, Selection of rules.Sole Moris
 
Creating wcf service in visual studio 2019-Installing environment for first time
Creating wcf service in visual studio 2019-Installing environment for first timeCreating wcf service in visual studio 2019-Installing environment for first time
Creating wcf service in visual studio 2019-Installing environment for first timeNILESH SAWARDEKAR
 
Flutter Tutorial For Beginners | Edureka
Flutter Tutorial For Beginners | EdurekaFlutter Tutorial For Beginners | Edureka
Flutter Tutorial For Beginners | EdurekaEdureka!
 
What is dotnet (.NET) ?
What is dotnet (.NET) ?What is dotnet (.NET) ?
What is dotnet (.NET) ?Talha Shahzad
 
Android Development with Kotlin, Part 1 - Introduction
Android Development with Kotlin, Part 1 - IntroductionAndroid Development with Kotlin, Part 1 - Introduction
Android Development with Kotlin, Part 1 - IntroductionAndreas Jakl
 
Android Programming Basics
Android Programming BasicsAndroid Programming Basics
Android Programming BasicsEueung Mulyana
 
ITkonekt 2019 | Robert C. Martin (Uncle Bob), Clean Architecture and Design
ITkonekt 2019 | Robert C. Martin (Uncle Bob), Clean Architecture and DesignITkonekt 2019 | Robert C. Martin (Uncle Bob), Clean Architecture and Design
ITkonekt 2019 | Robert C. Martin (Uncle Bob), Clean Architecture and DesignErginBilgin3
 

Was ist angesagt? (20)

Flutter Intro
Flutter IntroFlutter Intro
Flutter Intro
 
NodeJS
NodeJSNodeJS
NodeJS
 
Introduction to Node.js
Introduction to Node.jsIntroduction to Node.js
Introduction to Node.js
 
Flutter
FlutterFlutter
Flutter
 
Flutter presentation.pptx
Flutter presentation.pptxFlutter presentation.pptx
Flutter presentation.pptx
 
Flutter introduction
Flutter introductionFlutter introduction
Flutter introduction
 
What is flutter and why should i care?
What is flutter and why should i care?What is flutter and why should i care?
What is flutter and why should i care?
 
Hacking google cloud run
Hacking google cloud runHacking google cloud run
Hacking google cloud run
 
React - Introdução
React - IntroduçãoReact - Introdução
React - Introdução
 
Pune Flutter Presents - Flutter 101
Pune Flutter Presents - Flutter 101Pune Flutter Presents - Flutter 101
Pune Flutter Presents - Flutter 101
 
Analisis de Usabilidad GOMS: Goals, Objectives, Method, Selection of rules.
Analisis de Usabilidad GOMS: Goals, Objectives, Method, Selection of rules.Analisis de Usabilidad GOMS: Goals, Objectives, Method, Selection of rules.
Analisis de Usabilidad GOMS: Goals, Objectives, Method, Selection of rules.
 
Creating wcf service in visual studio 2019-Installing environment for first time
Creating wcf service in visual studio 2019-Installing environment for first timeCreating wcf service in visual studio 2019-Installing environment for first time
Creating wcf service in visual studio 2019-Installing environment for first time
 
Flutter Tutorial For Beginners | Edureka
Flutter Tutorial For Beginners | EdurekaFlutter Tutorial For Beginners | Edureka
Flutter Tutorial For Beginners | Edureka
 
What is dotnet (.NET) ?
What is dotnet (.NET) ?What is dotnet (.NET) ?
What is dotnet (.NET) ?
 
Android Development with Kotlin, Part 1 - Introduction
Android Development with Kotlin, Part 1 - IntroductionAndroid Development with Kotlin, Part 1 - Introduction
Android Development with Kotlin, Part 1 - Introduction
 
Android Programming Basics
Android Programming BasicsAndroid Programming Basics
Android Programming Basics
 
.Net Core
.Net Core.Net Core
.Net Core
 
ITkonekt 2019 | Robert C. Martin (Uncle Bob), Clean Architecture and Design
ITkonekt 2019 | Robert C. Martin (Uncle Bob), Clean Architecture and DesignITkonekt 2019 | Robert C. Martin (Uncle Bob), Clean Architecture and Design
ITkonekt 2019 | Robert C. Martin (Uncle Bob), Clean Architecture and Design
 
Vue JS Intro
Vue JS IntroVue JS Intro
Vue JS Intro
 
Introduction to MERN
Introduction to MERNIntroduction to MERN
Introduction to MERN
 

Andere mochten auch

Curso de javascript y node avanzado
Curso de javascript y node avanzadoCurso de javascript y node avanzado
Curso de javascript y node avanzadobrainybogota
 
Desarrollo web front-end con TypeScript, Angular 2 e Ionic
Desarrollo web front-end con TypeScript, Angular 2 e IonicDesarrollo web front-end con TypeScript, Angular 2 e Ionic
Desarrollo web front-end con TypeScript, Angular 2 e IonicMicael Gallego
 
Programación Asíncrona en Node JS
Programación Asíncrona en Node JSProgramación Asíncrona en Node JS
Programación Asíncrona en Node JSJavier Vélez Reyes
 
TypeScript - Angular 2 - ionic 2
TypeScript - Angular 2 - ionic 2TypeScript - Angular 2 - ionic 2
TypeScript - Angular 2 - ionic 2Micael Gallego
 
1/9 Curso JEE5, Soa, Web Services, ESB y XML
1/9 Curso JEE5, Soa, Web Services, ESB y XML1/9 Curso JEE5, Soa, Web Services, ESB y XML
1/9 Curso JEE5, Soa, Web Services, ESB y XMLJuan Carlos Rubio Pineda
 
Ebe2013: productividad conherramientas en la nube
Ebe2013: productividad conherramientas en la nubeEbe2013: productividad conherramientas en la nube
Ebe2013: productividad conherramientas en la nubeJuan Carlos Rubio Pineda
 
4/9 Curso JEE5, Soa, Web Services, ESB y XML
4/9 Curso JEE5, Soa, Web Services, ESB y XML4/9 Curso JEE5, Soa, Web Services, ESB y XML
4/9 Curso JEE5, Soa, Web Services, ESB y XMLJuan Carlos Rubio Pineda
 
8/9 Curso JEE5, Soa, Web Services, ESB y XML
8/9 Curso JEE5, Soa, Web Services, ESB y XML8/9 Curso JEE5, Soa, Web Services, ESB y XML
8/9 Curso JEE5, Soa, Web Services, ESB y XMLJuan Carlos Rubio Pineda
 
7/9 Curso JEE5, Soa, Web Services, ESB y XML
7/9 Curso JEE5, Soa, Web Services, ESB y XML7/9 Curso JEE5, Soa, Web Services, ESB y XML
7/9 Curso JEE5, Soa, Web Services, ESB y XMLJuan Carlos Rubio Pineda
 
Componentes Web y El Framework Polymer
Componentes Web y El Framework PolymerComponentes Web y El Framework Polymer
Componentes Web y El Framework PolymerJavier Vélez Reyes
 
144 Rest Web Services
144 Rest Web Services144 Rest Web Services
144 Rest Web ServicesGeneXus
 

Andere mochten auch (20)

Curso de javascript y node avanzado
Curso de javascript y node avanzadoCurso de javascript y node avanzado
Curso de javascript y node avanzado
 
Servidor API REST con Node.js
Servidor API REST con Node.jsServidor API REST con Node.js
Servidor API REST con Node.js
 
Desarrollo web front-end con TypeScript, Angular 2 e Ionic
Desarrollo web front-end con TypeScript, Angular 2 e IonicDesarrollo web front-end con TypeScript, Angular 2 e Ionic
Desarrollo web front-end con TypeScript, Angular 2 e Ionic
 
Programación Asíncrona en Node JS
Programación Asíncrona en Node JSProgramación Asíncrona en Node JS
Programación Asíncrona en Node JS
 
TypeScript - Angular 2 - ionic 2
TypeScript - Angular 2 - ionic 2TypeScript - Angular 2 - ionic 2
TypeScript - Angular 2 - ionic 2
 
Empezando con Angular 2
Empezando con Angular 2Empezando con Angular 2
Empezando con Angular 2
 
1/9 Curso JEE5, Soa, Web Services, ESB y XML
1/9 Curso JEE5, Soa, Web Services, ESB y XML1/9 Curso JEE5, Soa, Web Services, ESB y XML
1/9 Curso JEE5, Soa, Web Services, ESB y XML
 
Ebe2013: productividad conherramientas en la nube
Ebe2013: productividad conherramientas en la nubeEbe2013: productividad conherramientas en la nube
Ebe2013: productividad conherramientas en la nube
 
4/9 Curso JEE5, Soa, Web Services, ESB y XML
4/9 Curso JEE5, Soa, Web Services, ESB y XML4/9 Curso JEE5, Soa, Web Services, ESB y XML
4/9 Curso JEE5, Soa, Web Services, ESB y XML
 
Introduccion Servicios Web
Introduccion Servicios WebIntroduccion Servicios Web
Introduccion Servicios Web
 
8/9 Curso JEE5, Soa, Web Services, ESB y XML
8/9 Curso JEE5, Soa, Web Services, ESB y XML8/9 Curso JEE5, Soa, Web Services, ESB y XML
8/9 Curso JEE5, Soa, Web Services, ESB y XML
 
Servicios web xml
Servicios web xmlServicios web xml
Servicios web xml
 
SOA y Web Services
SOA y Web ServicesSOA y Web Services
SOA y Web Services
 
El Proyecto Polymer
El Proyecto PolymerEl Proyecto Polymer
El Proyecto Polymer
 
7/9 Curso JEE5, Soa, Web Services, ESB y XML
7/9 Curso JEE5, Soa, Web Services, ESB y XML7/9 Curso JEE5, Soa, Web Services, ESB y XML
7/9 Curso JEE5, Soa, Web Services, ESB y XML
 
3/9 soa y web services
3/9 soa y web services3/9 soa y web services
3/9 soa y web services
 
Componentes Web y El Framework Polymer
Componentes Web y El Framework PolymerComponentes Web y El Framework Polymer
Componentes Web y El Framework Polymer
 
Introducción a Node.js
Introducción a Node.jsIntroducción a Node.js
Introducción a Node.js
 
144 Rest Web Services
144 Rest Web Services144 Rest Web Services
144 Rest Web Services
 
Web services
Web servicesWeb services
Web services
 

Ähnlich wie Qué es Node.js

Code Blast 2012 - Node.js
Code Blast 2012 - Node.jsCode Blast 2012 - Node.js
Code Blast 2012 - Node.jsINSIGNIA4U
 
Evolución Android: Del Framework a la supervivencia del más fuerte
Evolución Android: Del Framework a la supervivencia del más fuerteEvolución Android: Del Framework a la supervivencia del más fuerte
Evolución Android: Del Framework a la supervivencia del más fuerteRubén Serrano Núñez
 
Inyección de dependencias en Node.js con InversifyJS & TypeScript
Inyección de dependencias en Node.js con  InversifyJS & TypeScriptInyección de dependencias en Node.js con  InversifyJS & TypeScript
Inyección de dependencias en Node.js con InversifyJS & TypeScriptRemo Jansen
 
Jruby On Rails. Ruby on Rails en la JVM
Jruby On Rails. Ruby on Rails en la JVMJruby On Rails. Ruby on Rails en la JVM
Jruby On Rails. Ruby on Rails en la JVMjavier ramirez
 
03 de Marzo 2015: Andrés Villarreal - Herramientas del Desarrollador Moderno
03 de Marzo 2015: Andrés Villarreal - Herramientas del Desarrollador Moderno03 de Marzo 2015: Andrés Villarreal - Herramientas del Desarrollador Moderno
03 de Marzo 2015: Andrés Villarreal - Herramientas del Desarrollador Modernowpargentina
 
Concurrencia y asincronía: Lenguajes, modelos y rendimiento: GDG Toledo Enero...
Concurrencia y asincronía: Lenguajes, modelos y rendimiento: GDG Toledo Enero...Concurrencia y asincronía: Lenguajes, modelos y rendimiento: GDG Toledo Enero...
Concurrencia y asincronía: Lenguajes, modelos y rendimiento: GDG Toledo Enero...Micael Gallego
 
Escalabilidad y alto rendimiento con Symfony2
Escalabilidad y alto rendimiento con Symfony2Escalabilidad y alto rendimiento con Symfony2
Escalabilidad y alto rendimiento con Symfony2Ricard Clau
 
Re evolución robótica
Re evolución robóticaRe evolución robótica
Re evolución robóticaSoftware Guru
 
Docker_K8S_lecciones_netcoreconf_2022.pdf
Docker_K8S_lecciones_netcoreconf_2022.pdfDocker_K8S_lecciones_netcoreconf_2022.pdf
Docker_K8S_lecciones_netcoreconf_2022.pdfLeonardo Micheloni
 
Kubernetes technical overview and our experience at Restorando :: Buenos Aire...
Kubernetes technical overview and our experience at Restorando :: Buenos Aire...Kubernetes technical overview and our experience at Restorando :: Buenos Aire...
Kubernetes technical overview and our experience at Restorando :: Buenos Aire...Restorando
 
Gwt seminario java_hispano_manolocarrasco
Gwt seminario java_hispano_manolocarrascoGwt seminario java_hispano_manolocarrasco
Gwt seminario java_hispano_manolocarrascoManuel Carrasco Moñino
 
Desarrollo de Aplicaciones con Node.js | INTERSYS UNPRG | 2012
Desarrollo de Aplicaciones con Node.js | INTERSYS UNPRG | 2012Desarrollo de Aplicaciones con Node.js | INTERSYS UNPRG | 2012
Desarrollo de Aplicaciones con Node.js | INTERSYS UNPRG | 2012Pilmee Gates
 
Preguntas test
Preguntas testPreguntas test
Preguntas testdalexis666
 
Preguntas test
Preguntas testPreguntas test
Preguntas testdalexis666
 

Ähnlich wie Qué es Node.js (20)

Devops episodio 1. devOpsTnf
Devops episodio 1. devOpsTnf Devops episodio 1. devOpsTnf
Devops episodio 1. devOpsTnf
 
Code Blast 2012 - Node.js
Code Blast 2012 - Node.jsCode Blast 2012 - Node.js
Code Blast 2012 - Node.js
 
MEAN ¿otro buzzword?
MEAN ¿otro buzzword?MEAN ¿otro buzzword?
MEAN ¿otro buzzword?
 
Evolución Android: Del Framework a la supervivencia del más fuerte
Evolución Android: Del Framework a la supervivencia del más fuerteEvolución Android: Del Framework a la supervivencia del más fuerte
Evolución Android: Del Framework a la supervivencia del más fuerte
 
Lenguaje de programación Java
Lenguaje de programación Java Lenguaje de programación Java
Lenguaje de programación Java
 
Inyección de dependencias en Node.js con InversifyJS & TypeScript
Inyección de dependencias en Node.js con  InversifyJS & TypeScriptInyección de dependencias en Node.js con  InversifyJS & TypeScript
Inyección de dependencias en Node.js con InversifyJS & TypeScript
 
JRuby al Rescate de J2EE
JRuby al Rescate de J2EEJRuby al Rescate de J2EE
JRuby al Rescate de J2EE
 
Jruby On Rails. Ruby on Rails en la JVM
Jruby On Rails. Ruby on Rails en la JVMJruby On Rails. Ruby on Rails en la JVM
Jruby On Rails. Ruby on Rails en la JVM
 
03 de Marzo 2015: Andrés Villarreal - Herramientas del Desarrollador Moderno
03 de Marzo 2015: Andrés Villarreal - Herramientas del Desarrollador Moderno03 de Marzo 2015: Andrés Villarreal - Herramientas del Desarrollador Moderno
03 de Marzo 2015: Andrés Villarreal - Herramientas del Desarrollador Moderno
 
Concurrencia y asincronía: Lenguajes, modelos y rendimiento: GDG Toledo Enero...
Concurrencia y asincronía: Lenguajes, modelos y rendimiento: GDG Toledo Enero...Concurrencia y asincronía: Lenguajes, modelos y rendimiento: GDG Toledo Enero...
Concurrencia y asincronía: Lenguajes, modelos y rendimiento: GDG Toledo Enero...
 
Escalabilidad y alto rendimiento con Symfony2
Escalabilidad y alto rendimiento con Symfony2Escalabilidad y alto rendimiento con Symfony2
Escalabilidad y alto rendimiento con Symfony2
 
Re evolución robótica
Re evolución robóticaRe evolución robótica
Re evolución robótica
 
Docker_K8S_lecciones_netcoreconf_2022.pdf
Docker_K8S_lecciones_netcoreconf_2022.pdfDocker_K8S_lecciones_netcoreconf_2022.pdf
Docker_K8S_lecciones_netcoreconf_2022.pdf
 
Kubernetes technical overview and our experience at Restorando :: Buenos Aire...
Kubernetes technical overview and our experience at Restorando :: Buenos Aire...Kubernetes technical overview and our experience at Restorando :: Buenos Aire...
Kubernetes technical overview and our experience at Restorando :: Buenos Aire...
 
Gwt seminario java_hispano_manolocarrasco
Gwt seminario java_hispano_manolocarrascoGwt seminario java_hispano_manolocarrasco
Gwt seminario java_hispano_manolocarrasco
 
Gwt I - entendiendo gwt
Gwt I - entendiendo gwtGwt I - entendiendo gwt
Gwt I - entendiendo gwt
 
Desarrollo de Aplicaciones con Node.js | INTERSYS UNPRG | 2012
Desarrollo de Aplicaciones con Node.js | INTERSYS UNPRG | 2012Desarrollo de Aplicaciones con Node.js | INTERSYS UNPRG | 2012
Desarrollo de Aplicaciones con Node.js | INTERSYS UNPRG | 2012
 
Preguntas test
Preguntas testPreguntas test
Preguntas test
 
Preguntas test
Preguntas testPreguntas test
Preguntas test
 
Java
Java Java
Java
 

Mehr von Redradix

Curso desarrollo frontend: HTML - CSS - jQuery
Curso desarrollo frontend: HTML - CSS - jQueryCurso desarrollo frontend: HTML - CSS - jQuery
Curso desarrollo frontend: HTML - CSS - jQueryRedradix
 
Redradix Weekend Textalytics
Redradix Weekend TextalyticsRedradix Weekend Textalytics
Redradix Weekend TextalyticsRedradix
 
Redradix Weekend: Animando sitios web
Redradix Weekend: Animando sitios web Redradix Weekend: Animando sitios web
Redradix Weekend: Animando sitios web Redradix
 
Curso responsive web design - Redradix School
Curso responsive web design  - Redradix SchoolCurso responsive web design  - Redradix School
Curso responsive web design - Redradix SchoolRedradix
 
Redradix school presentation
Redradix school presentationRedradix school presentation
Redradix school presentationRedradix
 
Qué es y como construir un MVP
Qué es y como construir un MVPQué es y como construir un MVP
Qué es y como construir un MVPRedradix
 
Emprender sin riesgos
Emprender sin riesgosEmprender sin riesgos
Emprender sin riesgosRedradix
 
Curso Javascript profesionales
Curso Javascript profesionalesCurso Javascript profesionales
Curso Javascript profesionalesRedradix
 
Presentación financiación startups redradix
Presentación financiación startups   redradixPresentación financiación startups   redradix
Presentación financiación startups redradixRedradix
 

Mehr von Redradix (9)

Curso desarrollo frontend: HTML - CSS - jQuery
Curso desarrollo frontend: HTML - CSS - jQueryCurso desarrollo frontend: HTML - CSS - jQuery
Curso desarrollo frontend: HTML - CSS - jQuery
 
Redradix Weekend Textalytics
Redradix Weekend TextalyticsRedradix Weekend Textalytics
Redradix Weekend Textalytics
 
Redradix Weekend: Animando sitios web
Redradix Weekend: Animando sitios web Redradix Weekend: Animando sitios web
Redradix Weekend: Animando sitios web
 
Curso responsive web design - Redradix School
Curso responsive web design  - Redradix SchoolCurso responsive web design  - Redradix School
Curso responsive web design - Redradix School
 
Redradix school presentation
Redradix school presentationRedradix school presentation
Redradix school presentation
 
Qué es y como construir un MVP
Qué es y como construir un MVPQué es y como construir un MVP
Qué es y como construir un MVP
 
Emprender sin riesgos
Emprender sin riesgosEmprender sin riesgos
Emprender sin riesgos
 
Curso Javascript profesionales
Curso Javascript profesionalesCurso Javascript profesionales
Curso Javascript profesionales
 
Presentación financiación startups redradix
Presentación financiación startups   redradixPresentación financiación startups   redradix
Presentación financiación startups redradix
 

Kürzlich hochgeladen

La era de la educación digital y sus desafios
La era de la educación digital y sus desafiosLa era de la educación digital y sus desafios
La era de la educación digital y sus desafiosFundación YOD YOD
 
El_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptx
El_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptxEl_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptx
El_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptxAlexander López
 
Hernandez_Hernandez_Practica web de la sesion 11.pptx
Hernandez_Hernandez_Practica web de la sesion 11.pptxHernandez_Hernandez_Practica web de la sesion 11.pptx
Hernandez_Hernandez_Practica web de la sesion 11.pptxJOSEMANUELHERNANDEZH11
 
dokumen.tips_36274588-sistema-heui-eui.ppt
dokumen.tips_36274588-sistema-heui-eui.pptdokumen.tips_36274588-sistema-heui-eui.ppt
dokumen.tips_36274588-sistema-heui-eui.pptMiguelAtencio10
 
Arenas Camacho-Practica tarea Sesión 12.pptx
Arenas Camacho-Practica tarea Sesión 12.pptxArenas Camacho-Practica tarea Sesión 12.pptx
Arenas Camacho-Practica tarea Sesión 12.pptxJOSEFERNANDOARENASCA
 
Crear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptx
Crear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptxCrear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptx
Crear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptxNombre Apellidos
 
Actividad integradora 6 CREAR UN RECURSO MULTIMEDIA
Actividad integradora 6    CREAR UN RECURSO MULTIMEDIAActividad integradora 6    CREAR UN RECURSO MULTIMEDIA
Actividad integradora 6 CREAR UN RECURSO MULTIMEDIA241531640
 
El uso de las tic en la vida ,lo importante que son
El uso de las tic en la vida ,lo importante  que sonEl uso de las tic en la vida ,lo importante  que son
El uso de las tic en la vida ,lo importante que son241514984
 
El uso de las TIC's en la vida cotidiana.
El uso de las TIC's en la vida cotidiana.El uso de las TIC's en la vida cotidiana.
El uso de las TIC's en la vida cotidiana.241514949
 
GonzalezGonzalez_Karina_M1S3AI6... .pptx
GonzalezGonzalez_Karina_M1S3AI6... .pptxGonzalezGonzalez_Karina_M1S3AI6... .pptx
GonzalezGonzalez_Karina_M1S3AI6... .pptx241523733
 
FloresMorales_Montserrath_M1S3AI6 (1).pptx
FloresMorales_Montserrath_M1S3AI6 (1).pptxFloresMorales_Montserrath_M1S3AI6 (1).pptx
FloresMorales_Montserrath_M1S3AI6 (1).pptx241522327
 
Presentación inteligencia artificial en la actualidad
Presentación inteligencia artificial en la actualidadPresentación inteligencia artificial en la actualidad
Presentación inteligencia artificial en la actualidadMiguelAngelVillanuev48
 
TEMA 2 PROTOCOLO DE EXTRACCION VEHICULAR.ppt
TEMA 2 PROTOCOLO DE EXTRACCION VEHICULAR.pptTEMA 2 PROTOCOLO DE EXTRACCION VEHICULAR.ppt
TEMA 2 PROTOCOLO DE EXTRACCION VEHICULAR.pptJavierHerrera662252
 
tics en la vida cotidiana prepa en linea modulo 1.pptx
tics en la vida cotidiana prepa en linea modulo 1.pptxtics en la vida cotidiana prepa en linea modulo 1.pptx
tics en la vida cotidiana prepa en linea modulo 1.pptxazmysanros90
 
Mapa-conceptual-del-Origen-del-Universo-3.pptx
Mapa-conceptual-del-Origen-del-Universo-3.pptxMapa-conceptual-del-Origen-del-Universo-3.pptx
Mapa-conceptual-del-Origen-del-Universo-3.pptxMidwarHenryLOZAFLORE
 
El uso delas tic en la vida cotidiana MFEL
El uso delas tic en la vida cotidiana MFELEl uso delas tic en la vida cotidiana MFEL
El uso delas tic en la vida cotidiana MFELmaryfer27m
 
PARTES DE UN OSCILOSCOPIO ANALOGICO .pdf
PARTES DE UN OSCILOSCOPIO ANALOGICO .pdfPARTES DE UN OSCILOSCOPIO ANALOGICO .pdf
PARTES DE UN OSCILOSCOPIO ANALOGICO .pdfSergioMendoza354770
 
LAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptx
LAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptxLAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptx
LAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptxAlexander López
 
R1600G CAT Variables de cargadores en mina
R1600G CAT Variables de cargadores en minaR1600G CAT Variables de cargadores en mina
R1600G CAT Variables de cargadores en minaarkananubis
 
Medidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptx
Medidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptxMedidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptx
Medidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptxaylincamaho
 

Kürzlich hochgeladen (20)

La era de la educación digital y sus desafios
La era de la educación digital y sus desafiosLa era de la educación digital y sus desafios
La era de la educación digital y sus desafios
 
El_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptx
El_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptxEl_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptx
El_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptx
 
Hernandez_Hernandez_Practica web de la sesion 11.pptx
Hernandez_Hernandez_Practica web de la sesion 11.pptxHernandez_Hernandez_Practica web de la sesion 11.pptx
Hernandez_Hernandez_Practica web de la sesion 11.pptx
 
dokumen.tips_36274588-sistema-heui-eui.ppt
dokumen.tips_36274588-sistema-heui-eui.pptdokumen.tips_36274588-sistema-heui-eui.ppt
dokumen.tips_36274588-sistema-heui-eui.ppt
 
Arenas Camacho-Practica tarea Sesión 12.pptx
Arenas Camacho-Practica tarea Sesión 12.pptxArenas Camacho-Practica tarea Sesión 12.pptx
Arenas Camacho-Practica tarea Sesión 12.pptx
 
Crear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptx
Crear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptxCrear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptx
Crear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptx
 
Actividad integradora 6 CREAR UN RECURSO MULTIMEDIA
Actividad integradora 6    CREAR UN RECURSO MULTIMEDIAActividad integradora 6    CREAR UN RECURSO MULTIMEDIA
Actividad integradora 6 CREAR UN RECURSO MULTIMEDIA
 
El uso de las tic en la vida ,lo importante que son
El uso de las tic en la vida ,lo importante  que sonEl uso de las tic en la vida ,lo importante  que son
El uso de las tic en la vida ,lo importante que son
 
El uso de las TIC's en la vida cotidiana.
El uso de las TIC's en la vida cotidiana.El uso de las TIC's en la vida cotidiana.
El uso de las TIC's en la vida cotidiana.
 
GonzalezGonzalez_Karina_M1S3AI6... .pptx
GonzalezGonzalez_Karina_M1S3AI6... .pptxGonzalezGonzalez_Karina_M1S3AI6... .pptx
GonzalezGonzalez_Karina_M1S3AI6... .pptx
 
FloresMorales_Montserrath_M1S3AI6 (1).pptx
FloresMorales_Montserrath_M1S3AI6 (1).pptxFloresMorales_Montserrath_M1S3AI6 (1).pptx
FloresMorales_Montserrath_M1S3AI6 (1).pptx
 
Presentación inteligencia artificial en la actualidad
Presentación inteligencia artificial en la actualidadPresentación inteligencia artificial en la actualidad
Presentación inteligencia artificial en la actualidad
 
TEMA 2 PROTOCOLO DE EXTRACCION VEHICULAR.ppt
TEMA 2 PROTOCOLO DE EXTRACCION VEHICULAR.pptTEMA 2 PROTOCOLO DE EXTRACCION VEHICULAR.ppt
TEMA 2 PROTOCOLO DE EXTRACCION VEHICULAR.ppt
 
tics en la vida cotidiana prepa en linea modulo 1.pptx
tics en la vida cotidiana prepa en linea modulo 1.pptxtics en la vida cotidiana prepa en linea modulo 1.pptx
tics en la vida cotidiana prepa en linea modulo 1.pptx
 
Mapa-conceptual-del-Origen-del-Universo-3.pptx
Mapa-conceptual-del-Origen-del-Universo-3.pptxMapa-conceptual-del-Origen-del-Universo-3.pptx
Mapa-conceptual-del-Origen-del-Universo-3.pptx
 
El uso delas tic en la vida cotidiana MFEL
El uso delas tic en la vida cotidiana MFELEl uso delas tic en la vida cotidiana MFEL
El uso delas tic en la vida cotidiana MFEL
 
PARTES DE UN OSCILOSCOPIO ANALOGICO .pdf
PARTES DE UN OSCILOSCOPIO ANALOGICO .pdfPARTES DE UN OSCILOSCOPIO ANALOGICO .pdf
PARTES DE UN OSCILOSCOPIO ANALOGICO .pdf
 
LAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptx
LAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptxLAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptx
LAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptx
 
R1600G CAT Variables de cargadores en mina
R1600G CAT Variables de cargadores en minaR1600G CAT Variables de cargadores en mina
R1600G CAT Variables de cargadores en mina
 
Medidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptx
Medidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptxMedidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptx
Medidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptx
 

Qué es Node.js

  • 1. Pero, ¿Qué es Node.js?
  • 2. ¿Qué es Node.js? Lo que todos sabemos • Hay Javascript por alguna parte • Backend • ¿Algo que ver con NoSQL? • Sirve para hacer chats
  • 3. ¿Qué es Node.js? Lo que no está tan claro • ¿Es un framework para Javascript? • ¿Es una librería? • ¿Qué tiene que ver v8 con Node.js? • ¿Para qué sirve (además de los chats)?
  • 4. ¿Qué es Node.js? Node.js es: • “Una plataforma de software usada para construir aplicaciones de red escalables (especialmente servidores). Node.js utiliza JavaScript como lenguaje, y alcanza alto rendimiento utilizando E/S no bloqueante y un bucle de eventos de una sola hebra”.
  • 5. ¿Qué es Node.js? Node.js es: • “Una plataforma de software usada para construir aplicaciones de red escalables (especialmente servidores). Node.js utiliza JavaScript como lenguaje, y alcanza alto rendimiento utilizando E/S no bloqueante y un bucle de eventos de una sola hebra”. • Wat?
  • 6. ¿Qué es Node.js? Por ejemplo, si... • ...para ruby tenemos Rails... • ...para python tenemos Django... • ...para php tenemos Symphony...
  • 7. ¿Qué es Node.js? Por ejemplo, si... • ...para ruby tenemos Rails... • ...para python tenemos Django... • ...para php tenemos Symphony... • ¿Podríamos decir que Node.js es el equivalente para JavaScript?
  • 9. ¿Qué es Node.js? ¿Qué es un “lenguaje de programación”?
  • 10. ¿Qué es Node.js? ¿Qué es un “lenguaje de programación”? • Una gramática que define la sintaxis del lenguaje • Un intérprete/compilador que lo sabe interpretar y ejecutar
  • 11. ¿Qué es Node.js? ¿Qué es un “lenguaje de programación”? • Una gramática que define la sintaxis del lenguaje • Un intérprete/compilador que lo sabe interpretar y ejecutar • Mecanismos para interactuar con el mundo exterior (llamadas al sistema)
  • 12. ¿Qué es Node.js? ¿Qué es un “lenguaje de programación”? • Una gramática que define la sintaxis del lenguaje • Un intérprete/compilador que lo sabe interpretar y ejecutar • Mecanismos para interactuar con el mundo exterior (llamadas al sistema) • Librería estándar (consola, ficheros, red, etc,...)
  • 13. ¿Qué es Node.js? ¿Qué es un “lenguaje de programación”? • Una gramática que define la sintaxis del lenguaje • Un intérprete/compilador que lo sabe interpretar y ejecutar • Mecanismos para interactuar con el mundo exterior (llamadas al sistema) • Librería estándar (consola, ficheros, red, etc,...) • Utilidades (intérprete interactivo, depurador, paquetes)
  • 14. ¿Qué es Node.js? v8 (JavaScript) • Una gramática que define la sintaxis del lenguaje • Un intérprete/compilador que lo sabe interpretar y ejecutar • Mecanismos para interactuar con el mundo exterior (llamadas al sistema) • Librería estándar (consola, ficheros, red, etc,...) • Utilidades (intérprete interactivo, depurador, paquetes) Node.js
  • 16. ¿Qué es Node.js? Node.js es algo más: • Una filosofía sobre cómo hacer las cosas • Un modelo de ejecución singular • Muy enfocado hacia aplicaciones de red
  • 17. ¿Qué es Node.js? Node.js es algo más: • Una filosofía sobre cómo hacer las cosas • Un modelo de ejecución singular • Muy enfocado hacia aplicaciones de red
  • 18. Una filosofía Node.js se crea con un objetivo en mente: • Escribir aplicaciones muy eficientes (E/S) con el lenguaje dinámico más rápido (v8) para soportar miles de conexiones simultáneas • Sin complicarse la vida innecesariamente - Sin paralelismo - Lenguaje sencillo y muy extendido - API muy pequeña y muy consistente - Apoyándose en Eventos y Callbacks
  • 19.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25. Una filosofía • No es la mejor opción para todos los casos - Si puedes hacerlo con Rails/Django/Spring, hazlo • Evita las “soluciones totales” - Una necesidad, una herramienta - Combina diferentes herramientas simples • Entiende lo que estás haciendo • Flexibilidad > magia - Tu código es tu responsabilidad - Cada aplicación es un mundo
  • 26.
  • 27.
  • 28.
  • 29. ¿Qué es Node.js? Node.js es algo más: • Una filosofía sobre cómo hacer las cosas • Un modelo de ejecución singular • Muy enfocado hacia aplicaciones de red
  • 30. Un modelo de ejecución Para entender Node.js, tenemos que entender dos ideas fundamentales: • Concurrencia vs. paralelismo (asincronía) • Eventos
  • 31. Un modelo de ejecución Concurrencia vs. Paralelismo • ¿Qué significa que dos cosas suceden “a la vez”?
  • 32. Un modelo de ejecución ¿Qué significa que dos cosas suceden “a la vez”?
  • 33. Un modelo de ejecución ¿Qué significa que dos cosas suceden “a la vez”?
  • 34. Un modelo de ejecución Concurrencia vs. Paralelismo • Paralelismo: varios actores realizando una acción cada uno simultáneamente • Concurrencia: un solo actor, con varias tareas “activas” entre las que va alternando
  • 35. Un modelo de ejecución Paralelismo
  • 36. Un modelo de ejecución Concurrencia
  • 37. Un modelo de ejecución La potencia de Node.js es (curiosamente): • Un modelo de ejecución concurrente - Muchos clientes o tareas activas • Pero NO paralelo - Una única hebra
  • 38. Un modelo de ejecución • ¿Qué ventajas tiene evitar el paralelismo? • ¿Qué desventajas? • Y por tanto, ¿Cuándo es útil el modelo de Node.js?
  • 39. Un modelo de ejecución Patrón Reactor • Un patrón de diseño para manejar eventos donde peticiones de servicio se transladan concurrentemente a un manejador central que se encarga de desmultiplexar las peticiones y despacharlas síncronamente mediante sus manejadores particulares asociados.
  • 40. Un modelo de ejecución Patrón Reactor • Un patrón de diseño para manejar eventos donde peticiones de servicio se transladan concurrentemente a un manejador central que se encarga de desmultiplexar las peticiones y despacharlas síncronamente mediante sus manejadores particulares asociados. • WAT??
  • 41. Un modelo de ejecución Patrón Reactor Bucle Principal
  • 42. Un modelo de ejecución Patrón Reactor Bucle Principal Mundo Exterior
  • 43. Un modelo de ejecución Patrón Reactor Suceso Bucle Principal Mundo Exterior
  • 44. Un modelo de ejecución Patrón Reactor Bucle Principal Suceso Mundo Exterior
  • 45. Un modelo de ejecución Patrón Reactor Suceso 2 Bucle Principal Suceso Mundo Exterior
  • 46. Un modelo de ejecución Patrón Reactor Bucle Principal Suceso Suceso 2 Mundo Exterior
  • 47. Un modelo de ejecución Patrón Reactor Tick! Bucle Principal Suceso Suceso 2 Mundo Exterior
  • 48. Un modelo de ejecución Patrón Reactor Tick! Bucle Principal Suceso Suceso 2 Mundo Exterior Evento: “Suceso”
  • 49. Un modelo de ejecución Patrón Reactor Tick! Evento: “Suceso” Bucle Principal Suceso Suceso 2 Mundo Exterior Manejadores
  • 50. Un modelo de ejecución Patrón Reactor Tick! Bucle Principal Suceso Suceso 2 Mundo Exterior Manejadores
  • 51. Un modelo de ejecución Patrón Reactor Suceso 3 Tick! Bucle Principal Suceso Suceso 2 Mundo Exterior Manejadores
  • 52. Un modelo de ejecución Patrón Reactor Tick! Bucle Principal Suceso Suceso 2 Mundo Exterior Suceso 3 Manejadores
  • 53. Un modelo de ejecución Patrón Reactor Tick! Listo! Bucle Principal Suceso Suceso 2 Mundo Exterior Suceso 3 Manejadores
  • 54. Un modelo de ejecución Patrón Reactor Tick! Bucle Principal Suceso Suceso 2 Mundo Exterior Suceso 3 Manejadores
  • 55. Un modelo de ejecución Patrón Reactor Tick! Evento: “Suceso 2” ??? Bucle Principal Suceso Suceso 2 Mundo Exterior Suceso 3 Manejadores
  • 56. Un modelo de ejecución Patrón Reactor Bucle Principal Suceso Suceso 2 Mundo Exterior Suceso 3 Manejadores
  • 57. Un modelo de ejecución Patrón Reactor • Programación contra eventos • Una vez puestos los manejadores, se pueden ejecutar en cualquier orden - El orden lo determina el orden en que aparezcan sucesos • La ejecución de los manejadores bloquea la hebra • Nunca hay dos manejadores ejecutándose al mismo tiempo • Muy eficiente... cuando E/S >> ejecuión del manejador
  • 58. ¿Qué es Node.js? Node.js es algo más: • Una filosofía sobre cómo hacer las cosas • Un modelo de ejecución singular • Muy enfocado hacia aplicaciones de red
  • 59. ¿Qué es Node.js? Muy enfocado hacia aplicaciones de red • ¿Por qué?
  • 60. ¿Qué es Node.js? Muy enfocado hacia aplicaciones de red • Mucha E/S - Por tanto, mucho tiempo con la CPU inactiva • Para aprovechar ese tiempo, necesitas otros clientes que lo puedan aprovechar • Similar a un camarero en un bar - Es un “Patron Reactor” del mundo real - Para aprovecharlo, tiene que haber varios clientes! - Un cliente no termina más deprisa por dedicarle un camarero sólo a él
  • 62. Ahora en serio: ¿Qué es Node.js? Necesitas: • Un ordenador • Un editor de texto • Saber abrir una consola/terminal • Tener node instalado (mejor si es v. 0.10) - Asegúrate de tener también npm - Como alternativa: http://c9.io • Saber manejarte con JavaScript
  • 63. Ahora en serio: ¿Qué es Node.js? console.log("Hola, Mundo!");
  • 64. Ahora en serio: ¿Qué es Node.js? $ node hola.js
  • 65. Ahora en serio: ¿Qué es Node.js?
  • 66. Ahora en serio: ¿Qué es Node.js? Pero... • ¿Y ese rollo del patrón Reactor? • ¿Por qué termina el programa en vez de quedarse escuchando sucesos en el bucle principal?
  • 67. Ahora en serio: ¿Qué es Node.js? Lo que pasa en realidad: • Node.js ejecuta todo tu código del tirón • Coloca los manejadores que hayas definido • Si no hay ningún manejador que se pueda ejecutar en el futuro, el programa termina!
  • 68. Ahora en serio: ¿Qué es Node.js? setTimeout(function() { console.log("Hola, Mundo del futuro!"); }, 1000);
  • 69. Ahora en serio: ¿Qué es Node.js? setInterval(function() { console.log("Hola otra vez, Mundo del futuro!"); }, 1000);
  • 70. Ahora en serio: ¿Qué es Node.js? setTimeout(function() { while (true); }, 100); setInterval(function() { console.log("Hola otra vez, Mundo del futuro!"); }, 1000);
  • 71. Ahora en serio: ¿Qué es Node.js? var start = Date.now(); setTimeout(function() { for (var i=Number.MAX_VALUE; i--;) { Math.pow(12345, 123455); } }, 100); setInterval(function() { var now = Date.now(); console.log("Han pasado", now - start, "ms"); start = now; console.log("Hola otra vez, Mundo del futuro!"); }, 1000);
  • 72. Ahora en serio: ¿Qué es Node.js? var start = Date.now(); setTimeout(function() { var timesLeft = Number.MAX_VALUE, r; (function unPoquitoMas() { if (timesLeft-- > 0) { r = Math.pow(12345, 123455); } setTimeout(unPoquitoMas, 0); }()); }, 100); setInterval(function() { var now = Date.now(); console.log("Han pasado", now - start, "ms"); start = now; console.log("Hola otra vez, Mundo del futuro!"); }, 1000);
  • 73. Ahora en serio: ¿Qué es Node.js? Nos surgen problemas curiosos... • ¿Excepciones?
  • 74. Ahora en serio: ¿Qué es Node.js? Nos surgen problemas curiosos... • ¿Excepciones? try { throw new Error("Peté!"); } catch(e) { console.log("Excepción!"); }
  • 75. Ahora en serio: ¿Qué es Node.js? Nos surgen problemas curiosos... • ¿Excepciones? try { setTimeout(function() { throw new Error("Peté!"); }, 0); } catch(e) { console.log("Excepción!"); }
  • 76. EventEmitter Nuestro código va a estar dirigido por eventos • Node.js tiene su propio “estándar” • Trae una implementación del patrón Observador (o Pub/Sub): EventEmitter • Todas sus librerías (y casi todos los paquetes) siguen este modelo
  • 77. EventEmitter var EventEmitter = require("events").EventEmitter; var pub = new EventEmitter(); pub.on("ev", function(m) { console.log("[ev]", m); }); pub.once("ev", function(m) { console.log("(ha sido la primera vez)"); }); pub.emit("ev", "Soy un Emisor de Eventos!"); pub.emit("ev", "Me vas a ver muy a menudo...");
  • 78. EventEmitter var EventEmitter = require("events").EventEmitter; var pub = new EventEmitter(); pub.on("ev", function(m) { console.log("[ev]", m); }); pub.once("ev", function(m) { console.log("(ha sido la primera vez)"); }); pub.emit("ev", "Soy un Emisor de Eventos!"); pub.emit("ev", "Me vas a ver muy a menudo...");
  • 79. require/exports require(<paquete o ruta>) • Importar módulos (paquetes, otros ficheros) • Garantía: una única vez • Devuelve el módulo!
  • 80. require/exports exports.propiedadPublica = <valor> • El otro lado del mecanismo • Se puede exportar cualquier valor
  • 81. require/exports codigo.js var lib = require("./libreria"); console.log(lib.propiedad); libreria.js console.log("una vez"); exports.propiedad = "Pública";
  • 82. Para que te confíes... Haz un módulo “reloj” • Que exporte una clase Reloj • Emita eventos “segundo”, “minuto” y “hora” var Reloj = require("./reloj").Reloj; var reloj = new Reloj(); reloj.on("segundo", function(fecha) { console.log("Un segundo! son las:", fecha); reloj.removeAllListeners("segundo"); });
  • 83. Para que te confíes... Un truco: • • require(“util”).inherits inherits(constructor, superConstructor) var inherits = require("util").inherits; function MiClase() { // ... } function MiSubClase() { // ... } inherits(MiSubClase, MiClase);
  • 84. JavaScript y el Universo El JavaScript del navegador vive en un mundo ideal • No hay SO con el que lidiar • No hay datos binarios • Todo es accesible con objetos y valores primitivos • Apenas hay E/S, siempre con valores simples (strings) • Un único usuario
  • 85. JavaScript y el Universo En Node.js, las cosas son de otra manera... • Llamadas al sistema • Mucha E/S • Datos binarios (ficheros, sockets, etc) • Descriptores de ficheros • Puertos • ...
  • 86. JavaScript y el Universo Tenemos que añadir nuevos conceptos a nuestro JS • Streams (lectura, escritura o duplex) • Buffers (representación de datos binarios) • Procesos • Rutas • http://nodejs.org/api/index.html
  • 87. JavaScript y el Universo Buffers • Una tira de bytes (datos binarios) • Similar a un array de enteros • Tamaño fijo • Manipular datos directamente - Sockets - Implementar protocolos complejos - Manipulación de ficheros/imágenes - Criptografía - ...
  • 88. JavaScript y el Universo Buffers var buf = new Buffer(100); buf.write("abcd", 0, 4, "ascii"); console.log(buf.toString("ascii"));
  • 89. JavaScript y el Universo Buffers Tamaño del buffer var buf = new Buffer(100); buf.write("abcd", 0, 4, "ascii"); console.log(buf.toString("ascii"));
  • 90. JavaScript y el Universo Posición Buffers Datos Longitud Codificación var buf = new Buffer(100); buf.write("abcd", 0, 4, "ascii"); console.log(buf.toString("ascii"));
  • 91. JavaScript y el Universo Buffers var buf = new Buffer(100); buf.write("abcd", 0, 4, "ascii"); console.log(buf.toString("ascii")); Codificación
  • 92. JavaScript y el Universo Buffers
  • 93. JavaScript y el Universo function Bitmap(w, h) { this.width = w; this.height = h; this.header = "P6n" + w + " " + h + "n255n"; this.buffer = new Buffer(w*h*3+this.header.length); this.buffer.write(this.header, 0, this.header.length, "ascii"); } Bitmap.prototype = { putPixel: function(x, y, color) { var pos = this.header.length + (y*this.width*3) + x*3; this.buffer.write(color, pos, 3, "hex"); }, fill: function(color) { this.buffer.fill(255, this.header.length); }, render: function() { process.stdout.write(this.buffer); } };
  • 94. JavaScript y el Universo var bitmap = new Bitmap(1, 1); bitmap.putPixel(0, 0, "000000"); bitmap.render();
  • 95. JavaScript y el Universo var bitmap = new Bitmap(10, 10); bitmap.fill("ffffff"); bitmap.putPixel(0, 0, "000000"); bitmap.render();
  • 96. JavaScript y el Universo Streams • “Chorros” de información - Lectura / Escritura / Duplex • Detrás de muchos mecanismos de Node.js - stdin/stdout - request HTTP - sockets - etc... • Instancias de EventEmitter • Acceso asíncrono
  • 97. JavaScript y el Universo Streams • Es raro crear streams directamente • Pero muchos recursos nos ofrecen este interfaz
  • 98. JavaScript y el Universo Streams de lectura • Entrada de datos • Eventos: - readable: hay datos para leer - data: se ha leído un trozo y está disponible - end: se agotó el stream - close: se cerró el stream - error: algo malo sucedió leyendo los datos
  • 99. JavaScript y el Universo Streams de lectura var fs = require("fs"); var readStream = fs.createReadStream("/etc/passwd", { flags: "r", encoding: "ascii", autoClose: true }); readStream.on("data", function(chunk) { console.log("He leído:", chunk.length); }); readStream.on("end", function() { console.log("ya está!"); });
  • 100. Una fácil Haz un programa que cuente las líneas de un fichero
  • 101. Una fácil Haz un programa que cuente las líneas de un fichero • Tienes los argumentos con los que se ha llamado al programa en process.argv
  • 102. JavaScript y el Universo Streams de escritura • Salida de datos • Operaciones: - write(chunk, [encoding], [callback]) end([chunk], [encoding], [callback]) • Eventos: - drain: el buffer del stream está vacío (puedes escribir más) - finish: se ha terminado de escribir toda la info y se ha cerrado el stream
  • 103. JavaScript y el Universo Streams de escritura var fs = require("fs"); var writeStream = fs.createWriteStream(process.argv[2], { flags: "w", encoding: "utf-8" }); for (var i=100; i--;) { writeStream.write(i + " líneas más para terminar...n"); } writeStream.end("FIN"); writeStream.on("finish", function() { console.log("Listo!"); });
  • 104. ¿Preguntas? Un buen momento para despejar dudas antes de seguir...
  • 106. HTTP Node.js trae un servidor web estupendo • Asíncrono - No bloquea la hebra - Cada cliente conectado consume muy poquitos recursos - Genial para miles de conexiones simultáneas • Relativamente rápido • Interfaz sencilla • HTTP puro y duro, sin adornos • Basado en streams y eventos
  • 107. HTTP var http = require("http"); var server = http.createServer(); server.on("request", function(req, res) { res.end("Hola, Mundo!"); }); server.listen(3000);
  • 108. El módulo HTTP var http = require("http"); var server = http.createServer(); server.on("request", function(req, res) { res.end("Hola, Mundo!"); }); server.listen(3000);
  • 109. HTTP var http = require("http"); Eventos! Respuesta (stream) var server = http.createServer(); server.on("request", function(req, res) { res.end("Hola, Mundo!"); }); server.listen(3000); Request (objeto)
  • 110. HTTP El servidor HTTP • Eventos: - connection request • Operaciones: - createServer([requestCallback]) listen(puerto, [hostname], [backlog], [callback]) close([callback])
  • 111. HTTP http.IncomingMessage (parametro “req”) • Representa la petición HTTP del cliente • Propiedaes: - req.headers: cabeceras de la petición - req.method: verbo HTTP - req.url: url de la petición - req.conneciton.remoteAddress: ip del cliente
  • 112. HTTP http.ServerResponse (parametro “res”) • Representa la respuesta del servidor • Stream de escritura • Operaciones adicionales: - res.writeHead(statusCode, [headers]): código HTTP y cabeceras de la respuesta - res.statusCode: [propiedad] Otra forma de establecer el código HTTP de la respuesta - res.setHeader(name, value): Otra forma de establecer las cabeceras, de una en una
  • 113. Manos a la obra Escribe un servidor web que devuelva la hora
  • 114. Un consejo: nodemon Para mejorar el flow de trabajo: • Editar, matar el proceso, volver a lanzarlo, probar... muy tedioso! • Instálate nodemon npm install -g nodemon • Reinicia el servidor cada vez que cambia el fichero $ nodemon <fichero.js>
  • 115. HTTP Node.js trae un módulo para parsear URLs var http = require("http"), url = require("url"), inspect = require("util").inspect; var server = http.createServer(); server.on("request", function(req, res) { var urlData = url.parse(req.url, true); res.end(inspect(urlData, {colors: false})); }); server.listen(3000);
  • 116. HTTP Node.js trae un módulo para parsear URLs var http = require("http"), url = require("url"), inspect = require("util").inspect; var server = http.createServer(); server.on("request", function(req, res) { var urlData = url.parse(req.url, true); res.end(inspect(urlData, {colors: false})); }); server.listen(3000);
  • 117. HTTP
  • 118. Un poco más difícil Escribe un servidor de ficheros • Lee el pathname de la URL • Busca un fichero con esa ruta dentro de ./public • Si existe, lo sirve • Si no existe, devuelve 404
  • 119. Un poco más difícil: notas fs.exists(filePath, callback) • Llama al callback con true si el fichero filePath existe • O con false si no existe var fs = require("fs"); fs.exists("./hola.txt", function(exists) { console.log(exists); });
  • 120. Un poco más difícil: notas fs.readFile(filePath, callback) • Otra manera de leer ficheros • Intenta leer filePath e invoca a callback con dos parámetros: - err: null si todo ha ido bien o, si hubo error, el error - data: todo el contenido del fichero (si fue posible leerlo) var fs = require("fs"); fs.readFile("./hola.txt", function(err, data) { if (err) { /* error! */ } console.log(data); });
  • 121. Un poco más difícil: epílogo ¿Cómo podríamos añadir un caché para no leer los ficheros del disco duro más de una vez? ¿Cómo podríamos hacer que los ficheros cacheados se liberaran después de x minutos? ¿Cómo podríamos escribir un registro de acceso?
  • 122. Un poco más difícil: variaciones Haz un contador de aperturas de emails • Sirviendo un .gif de 1x1 y contando cuantas veces lo sirves • Mejor aún, cuenta solo a cuántas IPs lo sirves Haz un servicio de avatares que cambie según: • La hora del día • La frecuencia con que es pedido (popularidad)
  • 123. Un poco más difícil: variaciones Haz un “servidor hellban” Si la IP está en la lista negra, todas las peticiones tienen un delay aleatorio que se va incrementando con cada petición consecutiva
  • 124. Servidor A/B Testing Cada vez que un cliente pide un recurso: • Se comprueba si ya tiene caso asignado (por IP) o se le asigna uno • Se construye la ruta del recurso según el caso asignado y se sirve • Si pide “success.png”, se marca su caso como exitoso y se le sirve la imágen • /stats devuelve un JSON con info sobre los casos (visitas totales y visitas exitosas por cada caso)
  • 125. Servidor A/B Testing Tenéis maqueta y recursos en : /tema1/abtesting
  • 127. Node.js y CPS Node.js maneja la asincronía utilizando callbacks • Continuation Passing Style • Continuaciones explícitas como funciones • “Cuando termines, ejecuta esta otra función”
  • 128. Node.js y CPS Los callbacks tienen muchas ventajas • Muy fáciles de entender e implementar • Familiares para el programador JavaScript • Extremadamente flexibles (clausuras, funciones de primer orden, etc, ...) • Un mecanismo universal de asincronía/continuaciones Pero...
  • 129. Node.js y CPS var fs = require("fs"); fs.exists("./hola.txt", function(exists) { if (exists) { fs.readFile("./hola.txt", function(err, data) { if (err) { // MANEJO DE ERROR } else { fs.writeFile("./copia.txt", data, function(err) { if (err) { // MANEJO DE ERROR } else { console.log("OK!"); } }) } }) } else { // MANEJO DE ERROR } });
  • 130. Node.js y CPS var fs = require("fs"); fs.exists("./hola.txt", function(exists) { if (exists) { fs.readFile("./hola.txt", function(err, data) { if (err) { // MANEJO DE ERROR } else { fs.writeFile("./copia.txt", data, function(err) { if (err) { // MANEJO DE ERROR } else { console.log("OK!"); } }) } }) } else { // MANEJO DE ERROR } });
  • 131. Node.js y CPS var fs = require("fs"); fs.exists("./hola.txt", function(exists) { if (exists) { fs.readFile("./hola.txt", function(err, data) { if (err) { // MANEJO DE ERROR } else { fs.writeFile("./copia.txt", data, function(err) { if (err) { // MANEJO DE ERROR } else { console.log("OK!"); } }) } }) } else { // MANEJO DE ERROR } });
  • 132. Node.js y CPS var fs = require("fs"); fs.exists("./hola.txt", function(exists) { if (exists) { fs.readFile("./hola.txt", function(err, data) { if (err) { // MANEJO DE ERROR } else { fs.writeFile("./copia.txt", data, function(err) { if (err) { // MANEJO DE ERROR } else { console.log("OK!"); } }) } }) } else { // MANEJO DE ERROR } }); Pyramid of Doom Callback Hell
  • 133. Node.js y CPS var fs = require("fs"); fs.exists("./hola.txt", function(exists) { if (exists) { fs.readFile("./hola.txt", function(err, data) { if (err) { // MANEJO DE ERROR } else { fs.writeFile("./copia.txt", data, function(err) { if (err) { // MANEJO DE ERROR } else { console.log("OK!"); } }) } }) } else { // MANEJO DE ERROR } });
  • 135. Promesas Una manera alternativa de modelar asincronía • Construcción explícita del flujo de ejecución • Separación en bloques consecutivos • Manejo de errores más controlado • Combinación de diferentes flujos asíncronos
  • 136. Promesas Una promesa = Un flujo de ejecución promesa.then(function() { // bloque return readFilePromise("./hola.txt"); }) .then(function(data) { // bloque return writeFilePromise("./copia.txt"); }) .then(function() { console.log("listo!"); }) .fail(function(err) { // MANEJO DEL ERROR });
  • 137. Promesas Una promesa = Un flujo de ejecución promesa.then(function() { // bloque return readFilePromise("./hola.txt"); }) .then(function(data) { // bloque return writeFilePromise("./copia.txt"); }) .then(function() { console.log("listo!"); }) .fail(function(err) { // MANEJO DEL ERROR });
  • 138. Promesas Una promesa = Un flujo de ejecución promesa.then(function() { // bloque return readFilePromise("./hola.txt"); }) .then(function(data) { // bloque return writeFilePromise("./copia.txt"); }) .then(function() { console.log("listo!"); }) .fail(function(err) { // MANEJO DEL ERROR });
  • 139. Promesas Una promesa = Un flujo de ejecución promesa.then(function() { // bloque return readFilePromise("./hola.txt"); }) .then(function(data) { // bloque return writeFilePromise("./copia.txt"); }) .then(function() { console.log("listo!"); }) .fail(function(err) { // MANEJO DEL ERROR });
  • 140. Promesas Una promesa = Un flujo de ejecución promesa.then(function() { // bloque return readFilePromise("./hola.txt"); }) .then(function(data) { // bloque return writeFilePromise("./copia.txt"); }) .then(function() { console.log("listo!"); }) .fail(function(err) { // MANEJO DEL ERROR });
  • 141. Promesas Una promesa = Un flujo de ejecución promesa.then(function() { // bloque return readFilePromise("./hola.txt"); }) .then(function(data) { // bloque return writeFilePromise("./copia.txt"); }) .then(function() { console.log("listo!"); }) .fail(function(err) { // MANEJO DEL ERROR });
  • 142. Promesas Una promesa = Un flujo de ejecución promesa.then(function() { // bloque return readFilePromise("./hola.txt"); }) .then(function(data) { // bloque return writeFilePromise("./copia.txt"); }) .then(function() { console.log("listo!"); }) .fail(function(err) { // MANEJO DEL ERROR });
  • 143. Promesas Una promesa = Un flujo de ejecución promesa.then(function() { // bloque return readFilePromise("./hola.txt"); }) .then(function(data) { // bloque return writeFilePromise("./copia.txt"); }) .then(function() { console.log("listo!"); }) .fail(function(err) { // MANEJO DEL ERROR });
  • 144. Promesas Una promesa = Un flujo de ejecución promesa.then(function() { // bloque return readFilePromise("./hola.txt"); }) .then(function(data) { // bloque return writeFilePromise("./copia.txt"); }) .then(function() { console.log("listo!"); }) .fail(function(err) { // MANEJO DEL ERROR });
  • 145. Promesas Una promesa = Un flujo de ejecución
  • 146. Promesas ¡Pero aún no hemos ejecutado nada! Solamente hemos construído el flujo
  • 147. Promesas ¿Ventajas? • Código mucho más ordenado y más legible • Mejor control de errores • Podemos manipular el flujo - Añadir nuevas etapas - Devolverlo en funciones - Pasarlo como parámetro • Podemos combinar varios flujos
  • 148. Promesas function copyFile(from, to) { return readFilePromise(from); .then(function(data) { // bloque return writeFilePromise(to); }); } copyFile("./hola.txt", "./copia.txt") .then(function() { return copyFile("./otraCosa.txt", "./copia2.txt"); }) .then(function() { console.log("listo!"); }) .fail(function(err) { console.log("Oops!"); })
  • 149. Promesas function copyFile(from, to) { return readFilePromise(from); .then(function(data) { // bloque return writeFilePromise(to); }); } copyFile("./hola.txt", "./copia.txt") .then(function() { return copyFile("./otraCosa.txt", "./copia2.txt"); }) .then(function() { console.log("listo!"); }) .fail(function(err) { console.log("Oops!"); })
  • 150. Promesas function copyFile(from, to) { return readFilePromise(from); .then(function(data) { // bloque return writeFilePromise(to); }); } copyFile("./hola.txt", "./copia.txt") .then(function() { return copyFile("./otraCosa.txt", "./copia2.txt"); }) .then(function() { console.log("listo!"); }) .fail(function(err) { console.log("Oops!"); })
  • 151. Promesas function copyFile(from, to) { return readFilePromise(from); .then(function(data) { // bloque return writeFilePromise(to); }); } copyFile("./hola.txt", "./copia.txt") .then(function() { return copyFile("./otraCosa.txt", "./copia2.txt"); }) .then(function() { console.log("listo!"); }) .fail(function(err) { console.log("Oops!"); })
  • 152. Promesas function copyFile(from, to) { return readFilePromise(from); .then(function(data) { // bloque return writeFilePromise(to); }); } copyFile("./hola.txt", "./copia.txt") .then(function() { return copyFile("./otraCosa.txt", "./copia2.txt"); }) .then(function() { console.log("listo!"); }) .fail(function(err) { console.log("Oops!"); })
  • 153. Promesas function copyFile(from, to) { return readFilePromise(from); .then(function(data) { // bloque return writeFilePromise(to); }); } copyFile("./hola.txt", "./copia.txt") .then(function() { return copyFile("./otraCosa.txt", "./copia2.txt"); }) .then(function() { console.log("listo!"); }) .fail(function(err) { console.log("Oops!"); })
  • 154. Promesas function copyFile(from, to) { return readFilePromise(from); .then(function(data) { // bloque return writeFilePromise(to); }); } copyFile("./hola.txt", "./copia.txt") .then(function() { return copyFile("./otraCosa.txt", "./copia2.txt"); }) .then(function() { console.log("listo!"); }) .fail(function(err) { console.log("Oops!"); })
  • 155. Promesas function copyFile(from, to) { return readFilePromise(from); .then(function(data) { // bloque return writeFilePromise(to); }); } copyFile("./hola.txt", "./copia.txt") .then(function() { return copyFile("./otraCosa.txt", "./copia2.txt"); }) .then(function() { console.log("listo!"); }) .fail(function(err) { console.log("Oops!"); })
  • 156. Promesas .then(success, [error]) • Concatena bloques • El nuevo bloque (success)... - Sólo se ejecuta si el anterior se ha ejecutado sin errores - Recibe como parámetro el resultado del bloque anterior - Devuelve el valor que se le pasará el siguiente bloque ➡ Si es un dato inmediato, se pasa tal cual ➡ Si es una promesa, se resuelve antes de llamar al siguiente bloque • El segundo parámetro pone un manejador de error - Equivalente a llamar a .fail(error) • .then(...) siempre devuelve una nueva promesa
  • 157. Promesas var promesa = readFilePromise("./hola.txt"); promesa = promesa.then(function(data) { console.log("Contenido del fichero: ", data); }, function(err) { console.log("Ooops!", err); })
  • 158. Promesas var promesa = readFilePromise("./hola.txt"); promesa = promesa.then(function(data) { console.log("Contenido del fichero: ", data); }, function(err) { console.log("Ooops!", err); })
  • 159. Promesas var promesa = readFilePromise("./hola.txt"); promesa = promesa.then(function(data) { console.log("Contenido del fichero: ", data); }, function(err) { console.log("Ooops!", err); })
  • 160. Promesas var promesa = readFilePromise("./hola.txt"); promesa = promesa.then(function(data) { console.log("Contenido del fichero: ", data); }, function(err) { console.log("Ooops!", err); })
  • 161. Promesas var promesa = readFilePromise("./hola.txt"); promesa = promesa.then(function(data) { console.log("Contenido del fichero: ", data); }, function(err) { console.log("Ooops!", err); })
  • 162. Promesas var promesa = readFilePromise("./hola.txt"); promesa = promesa.then(function(data) { console.log("Contenido del fichero: ", data); }, function(err) { console.log("Ooops!", err); })
  • 163. Promesas var promesa = readFilePromise("./hola.txt"); promesa = promesa.then(function(data) { console.log("Contenido del fichero: ", data); }, function(err) { console.log("Ooops!", err); })
  • 164. Promesas var promesa = readFilePromise("./hola.txt"); promesa.then(function(data) { return 1; }) .then(function(uno) { return 2; }, function(err) { console.log("Oh, oh..."); }) .then(function(dos) { return 3; }) .fail(function(err) { console.log("Oops!"); }); data 1 2 3
  • 165. Promesas var promesa = readFilePromise("./hola.txt"); var promesa2 = promesa.then(function(data) { return 1; }); var promesa3 = promesa.then(function(data) { return 2; }); data 1 data 2
  • 166. Promesas var promesa = readFilePromise("./hola.txt"); var promesa2 = promesa.then(function(data) { return 1; }); var promesa3 = promesa.then(function(data) { return 2; }); promesa3.then(function(dos) { console.log("Ping!"); }); data 1 data 2
  • 167. Promesas var promesa = readFilePromise("./hola.txt"); var promesa2 = promesa.then(function(data) { return 1; }); var promesa3 = promesa.then(function(data) { return 2; }); promesa3.then(function(dos) { console.log("Ping!"); }); promesa3.then(function(dos) { console.log("Pong!"); }); data 1 data 2 2
  • 168. Promesas var promesa = readFilePromise("./hola.txt"); var promesa2 = promesa.then(function(data) { return 1; }); var promesa3 = promesa.then(function(data) { return 2; }); promesa3.then(function(dos) { console.log("Ping!"); }); promesa3.then(function(dos) { console.log("Pong!"); }, function(err) { console.log("Oh, oh..."); }); data 1 data 2 2
  • 169. Promesas: walled garden Vamos a empezar a trastear con promesas... • Pero, de momento, con una librería de mentira • Para asentar conceptos • (y perder el miedo)
  • 170. Promesas: walled garden var fakePromise = require("./fakePromise"); var promise = fakePromise.gimmePromise(); promise.then(function() { return "hola"; }) .then(function(msg) { console.log(msg); return "mundo"; }) .then(function(msg) { console.log(msg); });
  • 171. Promesas: walled garden var fakePromise = require("./fakePromise"); var promise = fakePromise.gimmePromise(); promise.then(function() { return "hola"; }) .then(function(msg) { console.log(msg); return "mundo"; }) .then(function(msg) { console.log(msg); });
  • 172. Promesas: walled garden var fakePromise = require("./fakePromise"); var promise = fakePromise.gimmePromise(); promise.then(function() { return "hola"; }) .then(function(msg) { console.log(msg); return "mundo"; }) .then(function(msg) { console.log(msg); }); ¿Qué se muestra por consola al ejecutar esto?
  • 173. Promesas: walled garden var fakePromise = require("./fakePromise"); var promise = fakePromise.gimmePromise(); promise.then(function() { return "hola"; }) .then(function(msg) { console.log(msg); return "mundo"; }) .then(function(msg) { console.log(msg); }); ¿Por qué?
  • 174. Promesas: walled garden Una promesa = un flujo de ejecución • Configuramos un árbol de flujos e ejecución • Añadimos bloques a promesas • Pero... ¿Cuándo se empieza a ejecutar?
  • 175. Promesas: walled garden Una promesa = un flujo de ejecución • Configuramos un árbol de flujos e ejecución • Añadimos bloques a promesas • Pero... ¿Cuándo se empieza a ejecutar? • Cuando se resuelva la primera promesa del árbol
  • 176. Promesas: walled garden Las promesas se resuelven o se rechazan Si se resuelven: • Se resuelven a un valor (si es un bloque, su valor de retorno) • Representan el estado “OK, puede seguir el siguiente” Si se rechazan: • Representan un error • La ejecución cae hasta el siguiente manejador de errores • Se saltan todos los estados desde el error hasta el manejador
  • 177. Promesas: walled garden var fakePromise = require("./fakePromise"); var promise = fakePromise.gimmePromise(); promise.then(function() { return "hola"; }) .then(function(msg) { console.log(msg); return "mundo"; }) .then(function(msg) { console.log(msg); }); promise.resolve(42);
  • 178. Promesas: walled garden var fakePromise = require("./fakePromise"); var promise = fakePromise.gimmePromise(); promise.then(function() { return "hola"; }) .then(function(msg) { console.log(msg); return "mundo"; }) .then(function(msg) { console.log(msg); }) .fail(function(err) { console.log(err); return "MAL!" }) promise.reject(new Error("Oops!"));
  • 179. Promesas: walled garden Otra manera de rechazar una promesa es lanzar una excepción desde el interior de un bloque promise.then(function() { return "hola"; }) .then(function(msg) { console.log(msg); throw new Error("Oh, oh..."); return "mundo"; })
  • 180. Promesas: walled garden var fakePromise = require("./fakePromise"); var promise = fakePromise.gimmePromise(); promise.then(function() { return "hola"; }) .then(function(msg) { console.log(msg); throw new Error("Oh, oh..."); return "mundo"; }) .then(function(msg) { console.log(msg); }) .fail(function(err) { console.log(err); return "MAL!" }) promise.resolve(42);
  • 181. Promesas: walled garden var fakePromise = require("./fakePromise"); var promise = fakePromise.gimmePromise(); promise.then(function() { return "hola"; }) .then(function(msg) { console.log(msg); throw new Error("Oh, oh..."); return "mundo"; }) .then(function(msg) { console.log(msg); }) .fail(function(err) { console.log(err); return "MAL!" }) promise.resolve(42);
  • 182. Promesas: walled garden var fakePromise = require("./fakePromise"); var promise = fakePromise.gimmePromise(); promise.then(function() { return "hola"; }) .then(function(msg) { console.log(msg); throw new Error("Oh, oh..."); return "mundo"; }) .then(function(msg) { console.log(msg); }) .fail(function(err) { console.log(err); return "MAL!" }) promise.resolve(42);
  • 183. Promesas: walled garden var fakePromise = require("./fakePromise"); var promise = fakePromise.gimmePromise(); promise.then(function() { return "hola"; }) .then(function(msg) { console.log(msg); throw new Error("Oh, oh..."); return "mundo"; }) .then(function(msg) { console.log(msg); }) .fail(function(err) { console.log(err); return "MAL!" }) promise.resolve(42);
  • 184. Promesas: walled garden var fakePromise = require("./fakePromise"); var promise = fakePromise.gimmePromise(); promise.then(function(msg) { console.log(msg); throw new Error("Oh, oh..."); return "mundo"; }) .then(function(msg) { console.log(msg); return "OK!"; }) .fail(function(err) { console.log(err); return "MAL!"; }) .then(function(msg) { console.log(msg); }) promise.resolve("hola");
  • 185. Promesas: walled garden Propagación de promesas var fakePromise = require("./fakePromise"); var promise = fakePromise.gimmePromise(), promise2 = fakePromise.gimmePromise(); promise.then(function(val) { console.log("promise:", val); promise2.then(function(val) { console.log("promise2:", val); }); }); promise.resolve(42); setTimeout(promise2.resolve.bind(promise2, 12), 2000);
  • 186. Promesas: walled garden Propagación de promesas var fakePromise = require("./fakePromise"); var promise = fakePromise.gimmePromise(), promise2 = fakePromise.gimmePromise(); promise.then(function(val) { console.log("promise:", val); promise2.then(function(val) { console.log("promise2:", val); }); }); promise.resolve(42); setTimeout(promise2.resolve.bind(promise2, 12), 2000);
  • 187. Promesas: walled garden Propagación de promesas var fakePromise = require("./fakePromise"); var promise = fakePromise.gimmePromise(), promise2 = fakePromise.gimmePromise(); promise.then(function(val) { console.log("promise:", val); return promise2 }) .then(function(val) { console.log("promise2:", val); }); promise.resolve(42); setTimeout(promise2.resolve.bind(promise2, 12), 2000);
  • 188. Promesas: walled garden Propagación de promesas var fakePromise = require("./fakePromise"); var promise = fakePromise.gimmePromise(), promise2 = fakePromise.gimmePromise(); promise.then(function(val) { console.log("promise:", val); return promise2 }) .then(function(val) { console.log("promise2:", val); }); promise.resolve(42); setTimeout(promise2.resolve.bind(promise2, 12), 2000);
  • 189. Promesas: walled garden ¿Y al revés? var fakePromise = require("./fakePromise"); var promise = fakePromise.gimmePromise(), promise2 = fakePromise.gimmePromise(); promise.then(function(val) { console.log("promise:", val); return promise2 }) .then(function(val) { console.log("promise2:", val); }); setTimeout(promise.resolve.bind(promise, 42), 2000); promise2.resolve(12);
  • 190. Diferidos En el mundo real, las cosas son un poco distintas Las “promesas” están divididas en dos objetos: • La promesa en sí - Interfaz limitada a la construcción de flujos - .then, .fail y algún método más • Su diferido - Interfaz limitada a controlar el estado - .reject y .resolve
  • 191. Diferidos var Q = require("q"); var defer = Q.defer(), promise = defer.promise; // flujo promise.then(function(val) { console.log("val:", val); }, function(err) { console.log("Error!"); }); // estado defer.resolve(42);
  • 192. Diferidos var Q = require("q"); var defer = Q.defer(), promise = defer.promise; // flujo promise.then(function(val) { console.log("val:", val); }, function(err) { console.log("Error!"); }); // estado defer.resolve(42);
  • 193. Diferidos Cumplen dos roles diferentes: • Diferido lo controla el gestor del recurso/proceso que se está modelando • Promesa es el interfaz para que el consumidor del recurso pueda construir el flujo que necesita
  • 194. Promesas Un ejemplo realista: readFilePromise(file) • Implementa la función • Basándote en fs.readFile() • Devuelve una promesa • Se resuelve con el contenido del fichero • O se rechaza con el error
  • 195. Promesas function readFilePromise(filePath) { // ??? } readFilePromise("./hola.txt").then(function(contenido) { console.log("contenido:", contenido.toString()); }, function(err) { console.log("ERROR!", err); });
  • 196. Promesas var fs = require("fs"), Q = require("q"); function readFilePromise(filePath) { var defer = Q.defer(); fs.readFile(filePath, function(err, data) { err ? defer.reject(err) : defer.resolve(data); }) return defer.promise; } readFilePromise("./hola.txt").then(function(contenido) { console.log("contenido:", contenido.toString()); }, function(err) { console.log("ERROR!", err); });
  • 197. Promesas var fs = require("fs"), Q = require("q"); Gestor del recurso function readFilePromise(filePath) { var defer = Q.defer(); fs.readFile(filePath, function(err, data) { err ? defer.reject(err) : defer.resolve(data); }) return defer.promise; } Consumidor del recurso readFilePromise("./hola.txt").then(function(contenido) { console.log("contenido:", contenido.toString()); }, function(err) { console.log("ERROR!", err); });
  • 198. Promesas: paréntesis Intenta modificar el servidor de ficheros estáticos para que utilice promesas: • fileExistsPromise • readFilePromise
  • 199. Promesas: combinaciones Q es una librería muy potente • Trae un montón de funcionalidad • Muy recomendable leer la documentación • Nosotros vamos a ver dos cosas esenciales: - Combinación de promesas en paralelo - Adaptadores de llamadas con formato Node.js
  • 200. Q.all([promise, promise, ...]) Crea una promesa que representa al conjunto • La nueva promesa se resuelve cuando todas las del conjunto se hayan resuelto • Su valor es un array con los valores de las promesas • Si una del conjunto es rechazada, la nueva promesa también es rechazada
  • 201. Q.all([promise, promise, ...]) var Q = require("q"); var def1 = Q.defer(), prom1 = def1.promise, def2 = Q.defer(), prom2 = def2.promise; Q.all([prom1, prom2]).then(function(v) { console.log("Todas resueltas!"); console.log(v); // [42, 13] }) def1.resolve(42); setTimeout(def2.resolve.bind(def2, 13), 1000);
  • 203. Q.spread([v1, v2], callback) “Reparte” valores de un array en parámetros var Q = require("q"); Q.spread([1,2,3,4], function(a, b, c, d) { console.log(a, b, c, d); // 1 2 3 4 })
  • 204. Q.spread([v1, v2], callback) Invoca automáticamente a Q.all(...)! var Q = require("q"); var def1 = Q.defer(), def2 = Q.defer(), def3 = Q.defer(), pro1 = def1.promise, pro2 = def2.promise, pro3 = def3.promise; pro1.then(function(v1) { console.log(v1); return [pro2, pro3]; }) .spread(function(v2, v3) { console.log(v2, v3); }); def1.resolve(42); setTimeout(def2.resolve.bind(def2, 13), 1000); setTimeout(def3.resolve.bind(def3, 71), 200);
  • 205. Q.spread([v1, v2], callback) Invoca automáticamente a Q.all(...)! var Q = require("q"); var def1 = Q.defer(), def2 = Q.defer(), def3 = Q.defer(), pro1 = def1.promise, pro2 = def2.promise, pro3 = def3.promise; pro1.then(function(v1) { console.log(v1); return [pro2, pro3]; }) .spread(function(v2, v3) { console.log(v2, v3); }); def1.resolve(42); setTimeout(def2.resolve.bind(def2, 13), 1000); setTimeout(def3.resolve.bind(def3, 71), 200);
  • 206. Q.ninvoke(ctx, method, arg, [arg]) Adaptador para convertir llamadas Node.js en promesas var Q = require("q"), fs = require("fs"); Q.ninvoke(fs, "readFile", "./hola.txt") .then(function(data) { console.log("Contenido: ", data.toString()); }) .fail(function(err) { console.log("Oops!", err); });
  • 207. Q(promiseOrValue) Homogeneizar valores: • Si es una promesa, se queda tal cual • Si es un valor, se convierte en una promesa que se resuelve a ese valor Q(42).then(function(v) { console.log(v); // 42 }) Q(promise).then(function(v) { console.log(v); // resolución de promise })
  • 208. Promesas: gotcha ¿Qué pasa aquí? var d = Q.defer(), promise = d.promise; promise.then(function(v) { console.log(v); throw new Error("Vaya por Dios!"); }) .then(function() { console.log("Hola?"); }); d.resolve(42);
  • 209. promise.done() Finaliza el flujo, levantando los errores que no se hayan manejado var d = Q.defer(), promise = d.promise; promise.then(function(v) { console.log(v); throw new Error("Vaya por Dios!"); }) .then(function() { console.log("Hola?"); }) .done(); d.resolve(42);
  • 210. promise.done() La regla es: • Si vas a devolver la promesa, déjala abierta • Si eres el consumidor final de la promesa, asegúrate de cerrarla con .done()
  • 211. ¡A teclear! Vamos el primer ejercicio complicadillo: un servidor de ficheros versionado • La herramienta que hemos estado utilizando para compartir código • Con promesas • (Si alguien se atreve, que lo intente hacer sin promesas...)
  • 212. Necesitas saber... Manejar rutas: require(“path”) • path.resolve(base, ruta): ruta relativa a ruta absoluta (partiendo de base) • path.relative(base, relativa (desde base) ruta): ruta absoluta a ruta
  • 213. Necesitas saber... fs.readdir(ruta): Listar ficheros de un directorio • Devuelve un array de strings con los nombres de los ficheros • No es recursivo • No hay manera de saber si una entrada es un fichero o un directorio var fs = require("fs"), Q = require("q"); Q.ninvoke(fs, "readdir", ".").then(function(list) { console.log(list); })
  • 214. Necesitas saber... fs.stat(ruta): Info sobre un fichero/directorio • stats.isDirectory(): true si es un directorio • stats.mtime: Date de la última modificación var fs = require("fs"), Q = require("q"); Q.ninvoke(fs, "stat", ".").then(function(stats) { console.log("es dir?", stats.isDirectory()); console.log("última modificación:", stats.mtime); })
  • 215. Primer paso: listado recursivo Escribe una función listAllFiles(ruta) que: • Devuelva una promesa • La promesa se resuelva con un listado recursivo de todos los ficheros que hay dentro del directorio • Para cada fichero, genere un objeto del tipo {path: “/ruta/absoluta.txt”, stats: statsDelFichero} listAllFiles(".").then(function(list) { console.log(list); }) .done()
  • 216. Primer paso: listado recursivo
  • 217. Segundo paso: listener Función somethingChanged(ruta): • Devuelve true si algún fichero ha sido modificado desde la última vez que se invocó • Impleméntalo utilizando stats.mtime como referencia Utilizando esa función, escribe un “demonio” que monitorize un directorio y escriba un mensaje por consola cada vez que hay cambios
  • 218. Tercer paso: volcado a memoria Función readAllFiles(ruta): • Completa el resultado de listAllFiles() añadiendo una tercera propiedad “contents” con los contenidos del fichero Haz que el demonio lea todos los ficheros si detecta algún cambio y guarda el resultado en posiciones consecutivas de un array. Este va a ser nuestro control de versiones.
  • 219. Cuarto paso: sirve los datos El interfaz web tiene las siguientes rutas: • /: listado de versiones • /list?version=<n>: listado de ficheros de la versión n • /ruta/al/fichero?version=<n>: busca el fichero con la ruta correspondiente en la versión n y lo sirve • la versión “latest” siempre apunta a la versión más reciente
  • 220. Virguerías opcionanes Escribe un módulo simpleRoute de modo que podamos definir rutas así: var routes = require("./simpleRoute"); routes.get("/", function(req, res) { }) routes.get("/list", function(req, res) { }) routes.default(function(req, res) { })
  • 221. Virguerías opcionales Además, simpleRoute modifica el parámetro req.url y lo sustituye por la url parseada var routes = require("./simpleRoute"); routes.get("/list", function(req, res) { console.log(req.url.pathname); console.log(req.url.query.version); })
  • 223. ¿Qué es express? Un framework web para Node.js • Estrictamente web (microframework) • Sencillo y flexible • Muy popular • Se adapta muy bien a la filosofía de Node • Similar a Sinatra, Sylex, Flask, Spark, ...
  • 224. ¿Qué es express? Express nos va ayudar con... • Rutas • Parámetros • Formularios y subida de ficheros • Cookies • Sesiones • Templates
  • 225. ¿Qué es express? Express NO nos va ayudar con... • Base de datos / ORM • Autenticación de usuarios • Seguridad • Migraciones • Deployment • Organización del código
  • 226. ¿Qué es express? Más concretamente, express... • Construye sobre http • Procesando la petición por un stack de middleware que se encarga de decorar las peticiones - Asocia rutas a manejadores - Decorar los objetos req y res (parseo de parámetros, multipart, etc,...) - Rendear templates • Nosotros escogemos qué middlewares queremos usar, y en qué orden
  • 227. ¿Qué es express? var express = require("express"); var app = express(); // configuración + rutas app.listen(3000);
  • 228. ¿Qué es express? Equivalente a: var express = require("express"), http = require("http"); var app = express(); // configuración + rutas http.createServer(app).listen(3000);
  • 229. ¿Qué es express? Equivalente a: var express = require("express"), http = require("http"); var app = express(); // configuración + rutas http.createServer(app).listen(3000);
  • 230. ¿Qué es express? var app = require("express")(); app.get("/", function(req, res) { res.end("Hola desde express!"); }); app.listen(3000);
  • 231. ¿Qué es express? Verbo Ruta Manejador var app = require("express")(); app.get("/", function(req, res) { res.end("Hola desde express!"); }); app.listen(3000);
  • 232. ¿Qué es express? Objeto Stream var app = require("express")(); app.get("/", function(req, res) { res.end("Hola desde express!"); }); app.listen(3000);
  • 233. ¿Qué es express? ¿Qué nos aporta express, exactamente? • Depende de los middlewares que usemos! • Algunas cosas vienen por defecto
  • 234. Request req.params: parámetros de la ruta app.get("/user/:id", function(req, res) { req.params.id; });
  • 235. Request req.query: la querystring, parseada app.get("/search", function(req, res) { // GET /search?text=nodejs+express req.query.text; });
  • 236. Request • req.ip: IP del cliente conectado • req.host: Hostname del servidor • req.xhr: ¿Es ajax? • req.acceptedLanguages: Array de locales • req.host: Hostname del servidor • Mas info en http://express.js.com/api.html
  • 237. Response res.cookie(nombre, valor, [opciones]) • Modifica la cookie “nombre” con el valor “valor” res.cookie("visitas", "1", {domain: ".ejemplo.com"});
  • 238. Response res.redirect([status], url) • Redirige a url • El código de estado es opcional (302 por defecto) res.redirect(301, "http://www.google.com");
  • 239. Response res.send([status], body) • Envía una respuesta (escribe en el buffer) • Lo más adecuado para respuestas sencillas - no streaming • Automatiza ciertas cabeceras - Content-Type, Content-Length • Convierte objetos a JSON res.send(500, {msg: "Oh, oh..."});
  • 240. Response Muchos otros métodos auxiliares: • Cabeceras • Envío de ficheros • JSONP • Content-Type • ¡Lee la documentación! http://expressjs.com/api.html
  • 241. Application app.configure([entorno], callback) • Configurar la aplicación • Opcionalmente: configuración para un entorno - “development” - “testing” - “production” - etc app.configure('development', function(){ app.set('db uri', 'localhost/dev'); })
  • 242. Application ¿Qué significa “configurar la aplicación”? • Crear algunas propiedades globales • Especificar el stack de middleware
  • 243. Application app.set(prop, value) / app.get(prop) • Escribe/consulta valores globales en app • Básicamente inútil... • Excepto para cambiar alguna configuración más avanzada app.set('title', 'Redradix'); app.get('title');
  • 244. Middleware Middleware son módulos “plug and play” que se pueden apilar arbitrariamente en cualquier orden y proveen cierta funcionalidad • Filtros: procesan tráfico entrate/saliente, pero no responden a ninguna request. (ejemplo: bodyParser) • Proveedores: ofrecen respuestas automáticas a algún tipo de petición (ejemplo: static provider)
  • 248. Middleware Express trae unos cuantos preinstalados • http://www.senchalabs.org/connect/ Una lista de módulos de terceros • https://github.com/senchalabs/connect/wiki
  • 249. express.favicon(ruta) Sirve el favicon de la aplicación • Debe ser el primero • Para evitar capas inncesarias • log • parseo • cookies • etc...
  • 250. express.logger([opciones]) Registro de actividad • Muchas opciones... http://www.senchalabs.org/connect/logger.html • Se suele poner debajo de express.favicon()
  • 251. express.cookieParser([secret]) Parsea las cookies de la petición • Opcional: firmar cookies con secret • Crea los objetos req.cookies y req.signedCookies app.configure(function(){ app.use(express.cookieParser('secreto')); }) app.get("/", function(req, res) { console.log(req.cookies); console.log(req.signedCookies); res.send(200); })
  • 252. express.bodyParser() Parsea el cuerpo de las peticiones POST • Decodifica - application/json - application/x-www-form-urlencoded - multipart/form-data • Crea el objeto req.body con los parámetros POST • Crea el objeto req.files con los ficheros que se han subido desde un formulario
  • 253. express.cookieSession([opciones]) Inicializa y parsea los datos de sesión del usuario • Crea el objeto req.session • Utilizando cookies como almacenamiento • Opciones: - secret: firma de segurdad para la cookie - maxAge: duración, en ms (default: sin caducidad) - path: ruta para la que es válida la cookie (default: /) - httpOnly: protegida del cliente (default: true)
  • 254. express.cookieSession([opciones]) var express = require("express"), app = express(); app.configure(function(){ app.use(express.cookieParser('secreto')); app.use(express.cookieSession()); }) app.get("/", function(req, res) { req.session.visitas || (req.session.visitas = 0); var n = req.session.visitas++; res.send("Me has visitado: " + n + " veces!"); }) app.listen(3000);
  • 255. express.static(dir) Sirve los ficheros estáticos dentro de dir • ¡Muy útil! Se pone cerca del final • Cachea los ficheros • La variable global __dirname contiene el directorio donde reside el script en ejecución
  • 256. app.router El enrutado de la aplicación • Sirve para específicar exáctamente en qué momento quieres que se procesen las rutas de tu app
  • 258. Templates Express tiene un mecanismo para rendear templates • Agnóstico • Modular • Simple • NO trae ningún motor de templates por defecto
  • 259. Templates res.render(view, [locals], callback) • view: ruta del template • locals: valores a interpolar • callback: function(err, html) { ... }
  • 260. Templates Tenemos muchos motores de templates para elegir! https://npmjs.org/browse/keyword/template • Haml • Hogan/Mustache • Twig/Swig • Ejs • Jinja • Jade ....
  • 261. Ejs <h1><%= title %></h1> <ul> <% for(var i=0; i<supplies.length; i++) { %> <li> <a href='supplies/<%= supplies[i] %>'> <%= supplies[i] %> </a> </li> <% } %> </ul>
  • 262. Jade
  • 263. Templates Algunas propuestas originales/innovadoras: CoffeeKup: http://coffeekup.org/ Weld: https://github.com/tmpvar/weld Domo: http://domo-js.com/
  • 264. Templates Para utilizarlos desde express: var express = require("express"), app = express(); app.configure(function() { app.engine("jade", require("jade").__express); app.set("views", "./views"); app.set("view engine", "jade"); }) app.get("/", function(req, res) { res.render("welcome", {user: "Pepito"}); }) app.listen(3000)
  • 265. Templates Para utilizarlos desde express: var express = require("express"), app = express(); app.configure(function() { app.engine("jade", require("jade").__express); app.set("views", "./views"); app.set("view engine", "jade"); }) app.get("/", function(req, res) { res.render("welcome", {user: "Pepito"}); }) app.listen(3000)
  • 266. Templates Para utilizarlos desde express: var express = require("express"), app = express(); app.configure(function() { app.engine("jade", require("jade").__express); app.set("views", "./views"); app.set("view engine", "jade"); }) app.get("/", function(req, res) { res.render("welcome", {user: "Pepito"}); }) app.listen(3000)
  • 267. Templates Para utilizarlos desde express: var express = require("express"), app = express(); app.configure(function() { app.engine("jade", require("jade").__express); app.set("views", "./views"); app.set("view engine", "jade"); }) app.get("/", function(req, res) { res.render("welcome", {user: "Pepito"}); }) app.listen(3000)
  • 268. Templates Para utilizarlos desde express: var express = require("express"), app = express(); app.configure(function() { app.engine("jade", require("jade").__express); app.set("views", "./views"); app.set("view engine", "jade"); }) app.get("/", function(req, res) { res.render("welcome", {user: "Pepito"}); }) app.listen(3000)
  • 269. Templates Donde /views/welcome.jade sería algo así: doctype 5 html(lang="es") body h1 Bienvenido, #{user}
  • 270. Templates Express es bastante listo (buenos defaults) var express = require("express"), app = express(); app.get("/", function(req, res) { res.render("welcome.jade", {user: "Pepito"}); }) app.listen(3000)
  • 271. Blog en 15 minutos Vamos a poner en práctica lo que hemos visto • Vamos a hacer un “blog”, simplificado • Sin BBDD • Tiene 7 rutas (4 pantallas) - GET /posts - GET /posts/new - POST /posts - GET /posts/:id - GET /posts/edit - PUT /posts/:id - DEL /posts/:id
  • 272. Blog en 15 minutos ¿Sin base de datos? • Guarda los datos en memoria, en un array • Cuidado si usas nodemon, porque al guardar se resetea el servidor y se pierde el contenido del array
  • 273. Un truco: app.param(...) Mapear parámetros de url app.param("postid", function(req, res, next, postId) { req.post = posts.find(postId); if (err || !req.post) { next(err || new Error("Post no encontrado ("+postId+")")); } else { next(); } }); app.get("/posts/:postid", function(req, res) { console.log(req.post); });
  • 274. Más Middleware Escribir middleware para express es muy sencillo: • Una función que recibe tres parámetros: - req - res - next • Al terminar su tarea, tiene que invocar a next() - Sin parámetro: se invoca al siguiente middleware del stack - Con parámetro: se cambia la ruta a lo que se pase como parámetro
  • 275. Más Middleware Por ejemplo, un logger simple: app.configure(function(){ app.use(function(req, res, next) { console.log(" * %s: %s %s", req.connection.remoteAddress, req.method, req.url); next(); }); })
  • 276. Más Middleware Haz un logger que muestre: • Hora de la petición • IP • Método • Ruta • Tiempo de respuesta de la petición Pero solo si el tiempo de respuesta está muy por encima de la media!
  • 277. Más Middleware Haz un módulo de “mensajes flash” • Mensajes que solo están disponibles para la siguiente request • Muy útiles para informar de ciertos sucesos - “Post creado con éxito” - “Email enviado” - Errores - etc...
  • 278. Más Middleware app.post("/posts", function(req, res) { var post = createPost(req.body); if (post) { req.flash.message("Post creado con éxito!"); } else { req.flash.error("No se ha podido crear..."); } res.redirect("/posts/" + post.id); }) app.get("/posts/:postid", function(req, res) { console.log(req.flash.message()); console.log(req.flash.error()); })
  • 279. Más Middleware app.post("/posts", function(req, res) { var post = createPost(req.body); if (post) { req.flash.message("Post creado con éxito!"); } else { req.flash.error("No se ha podido crear..."); } res.redirect("/posts/" + post.id); }) app.get("/posts/:postid", function(req, res) { console.log(req.flash.message()); console.log(req.flash.error()); })
  • 280. Más Middleware app.post("/posts", function(req, res) { var post = createPost(req.body); if (post) { req.flash.message("Post creado con éxito!"); } else { req.flash.error("No se ha podido crear..."); } res.redirect("/posts/" + post.id); }) app.get("/posts/:postid", function(req, res) { console.log(req.flash.message()); console.log(req.flash.error()); })
  • 281. Más Middleware: Errores Un caso especial de middleware: una función que reciba un parámetro más var express = require("express"), app = express(); app.get("/", function(req, res) { throw new Error("??") }) app.use(function(err, req, res, next) { console.log("* SOCORRO! ALGO VA MAL! ", err); res.send(500) }) app.listen(3000)
  • 282. Más Middleware: Errores Más correcto así: var express = require("express"), app = express(); app.get("/", function(req, res, next) { next(new Error("esta app no hace nada")); }) app.use(function(err, req, res, next) { console.log("* SOCORRO! ALGO VA MAL! ", err); res.send(500) }) app.listen(3000)
  • 283. Más Middleware: Errores Escribe un manejador de errores para el blog • Página 404 (página no encontrada o ruta no existe) • Página 500 (mostrar en caso de error/excepción) • Registra la fecha, la ruta y el mensaje de error en errors.log
  • 284. Más Middleware Dos maneras de activar middlewares • Globalmente (app.use), activos para toda la app • Locales para ciertas rutas
  • 285. Más Middleware var express = require("express"), app = express() function logThis(req, res, next) { console.log(" -> ", req.url); next(); } app.get("/", logThis, function(req, res) { res.send("Ok!"); }) app.listen(3000);
  • 286. Más Middleware Afinar la funcionalidad de cada ruta • Pre-procesado de URL (parámetros, formato) • Seguridad • Caché • Métricas
  • 287. Más Middleware Escribe un módulo para cachear la respuesta de una ruta durante X ms Duración en ms app.get("/", new StaticCache(5*1000), function(req, res) { res.send("Envío mi respuesta..." + new Date()); })
  • 288. Más Middleware Una variación: • Cachea a un fichero • Sustituye res.write por el stream del fichero • Cuando se dispare “finish”, sirve lo que has almacenado • Puedes añadirlo al ejercicio del Blog para cachear las páginas de detalle de un post
  • 289. SimpleAuth Módulo para autenticar usuarios simpleAuth.js • Sencillo y flexible • Independiente de la BBDD • Utilizando las sesiones de express • Middleware de ruta
  • 290. SimpleAuth El módulo provee 3 middlewares: • createSession: comprueba los credenciales de usuario y crea una nueva sesión si son correctos • requiresSession: protege una ruta y solo deja pasar a usuarios autenticados • destroySession: desloguea a un usuario
  • 291. SimpleAuth var auth = require("./simpleAuth"); app.post("/login", auth.createSession({ redirect: "/secret" })) app.get("/secret", auth.requiresSession, function(req, res) { res.end("Hola, " + req.user.email); }) app.get("/logout", auth.destroySession, function(req, res) { res.end("Te has deslogueado"); });
  • 292. SimpleAuth El usuario ha de definir una estrategia: serializeUser: ¿Cómo serializar un usuario? deserializeUser: ¿Cómo des-serializar un usuario? checkCredentials: ¿Son correctos los credenciales?
  • 293. SimpleAuth var auth = require("./simpleAuth") var users = [{email: "admin@asdf.com", pass: "asdf", id: 0}]; auth.setStrategy({ serializeUser: function(user) { return user.id; }, deserializeUser: function(userId, cb) { cb(users[userId]); }, checkCredentials: function(email, pass, cb) { var admin = users[0]; if (email === admin.email && pass === admin.pass) { cb(null, admin); } else { cb(null, false); } } });
  • 294. SimpleAuth auth.createSession(config) • config.username: nombre del campo del formulario • config.password: nombre del campo del formulario • config.redirect: URL en caso de éxito • config.failRedirect: URL en caso de error - default: /login • Devuelve: una función para usar como middleware
  • 295. SimpleAuth auth.createSession(config) • Genera una función que... 1. Extrae username y password según config de req.body 2. Llama a strategy.checkCredentials con username y password 3. Si son credenciales correctos: 3.1. Crea una entrada en la sessión o cookie con el resultado de llamar a strategy.serializeUser(usuario), donde usuario es lo que devuelve strategy.checkCredentials 3.2. Redirige a la URL de éxito 4. Si no son correctos: 4.1. Redirige a la URL de error
  • 296. SimpleAuth auth.requiresSession 1. Se asegura de que exista la entrada adecuada en la sesión o cookie 2. Llama a strategy.deserializeUser con el valor 3. Guarda el usuario des-serializado en req.user 4. En caso de error, borra la sesión o cookie
  • 297. SimpleAuth auth.destroySession 1. Borra la entrada adecuada en la sesión o cookie
  • 298. SimpleAuth auth.setStrategy(strategy) 1. Configura la estretagia: - strategy.serializeUser - strategy.deserializeUser - strategy.checkCredentials - strategy.loginRoute
  • 299. SimpleAuth Protege la edición del blog con simpleAuth • Utilizando un array de usuarios escrito directamente en el código • Página de login + link logout + solo los usuarios logueados pueden crear o editar posts • Si te sobra tiempo, haz que los usuarios se puedan registrar
  • 300. Redis
  • 301. ¿Qué es Redis? Una base de datos NoSQL... • Clave/valor • Extremadamente rápida • Persistencia y transacciones • Estructuras de datos - Listas - Conjuntos - Hashes • Pub/sub
  • 302. ¿Qué es Redis? Un servidor de estructuras de datos • Sin tablas, ni queries ni JOINs • Una manera de pensar muy diferente • Muchas queries muy rápidas • Muy fácil de aprender, muy fácil de usar mal
  • 303. ¿Qué es Redis? Redis es una opción estupenda para... • Datos de acceso inmediato - Sesiones - Cachés • Escrituras muy rápidas - Logs - Conteos y estadísticas • Canal de comunicación pub/sub - Comunicación entre procesos (workers, big data) - Chats :)
  • 304. ¿Qué NO es Redis? Redis es una opción terrible para... • Relaciones de datos complejas • Búsquedas
  • 305. ¿Para qué sirve? Mi consejo: • NO bases tu app en redis • A no ser que sea muy simple o sepas muy bien lo que haces • Utiliza Redis como complemento a tu BBDD - Cachés - Estadísticas - Workers - Sesiones - Registros - Colas
  • 306. La idea general Redis es un gran Hash de claves • No hay concepto de tabla/colección • El valor fundamental es la cadena (string) • Pero una clave puede contener un valor más complejo - Hashes (string -> string) - Listas (de strings) - Sets (de strings) - Sets ordenados (de strings)
  • 307. Requisitos Necesitas: • Tener instalado y arrancado redis • Tener a mano la documentación ➡ http://redis.io/commands • npm install hiredis redis • Como alternativa: ➡ c9.io - nada-nix install redis npm install hiredis redis redis-server --port 16379 --bind $IP
  • 308. Primeros pasos Primero, necesitas conectarte al servidor var redis = require("redis"); var client = redis.createClient(); // c9.io: // var client = redis.createClient(16379, process.env.IP);
  • 309. Primeros pasos Ahora puedes mandar comandos a Redis • • client.nombreDelComando(arg, callback) • callback: function(err, value) { } client.nombreDelComando(arg1, arg2, callback)
  • 310. SET / GET Guardar y recuperar un valor var redis = require("redis"), client = redis.createClient(); client.set("miClave", "miValor", function(err, val) { console.log(arguments); client.get("miClave", function(err, value) { console.log("valor: ", value); }); })
  • 311. SET / GET Guardar y recuperar un valor var redis = require("redis"), client = redis.createClient(); client.set("miClave", "miValor", function(err, val) { console.log(arguments); client.get("miClave", redis.print); })
  • 312. SET / GET Guardar y recuperar un valor var redis = require("redis"), client = redis.createClient(); client.set("miClave", "miValor", function(err, val) { console.log(arguments); client.get("miClave", function(err, value) { console.log("valor: ", value); }); })
  • 313. SET / GET Guardar y recuperar un valor var redis = require("redis"), client = redis.createClient(); client.set("miClave", "miValor", function(err, val) { console.log(arguments); client.get("miClave", function(err, value) { console.log("valor: ", value); }); })
  • 314. SET / GET Guardar y recuperar un valor var redis = require("redis"), Q = require("q"), client = redis.createClient(); Q.ninvoke(client, "set", "miClave", "miValor") .then(function() { return Q.ninvoke(client, "get", "miClave"); }) .then(function(value) { console.log("Valor: ", value); }) .done();
  • 315. SET / GET Guardar y recuperar un valor
  • 316. Un truco: redis-cli monitor Para ver qué está pasando en Redis
  • 317. Redis = Strings!! Cuidado con los valores. Han de ser siempre strings. var redis = require("redis"), Q = require("q"), client = redis.createClient(); Q.ninvoke(client, "set", "miClave", {un: "objeto"}) .then(function() { return Q.ninvoke(client, "get", "miClave"); }) .then(function(value) { console.log("Valor: ", value); // Valor: [object Object] }) .done();
  • 318. Redis = Strings!! var obj = {propiedad: "valor"}; Q.ninvoke(client, "set", "miClave", JSON.stringify(obj)) .then(function() { return Q.ninvoke(client, "get", "miClave"); }) .then(function(value) { console.log("Valor: ", value); // Valor: {"propiedad": "valor"} }) .done();
  • 319. Redis = Strings!! var serializable = { toString: function() { return JSON.stringify(this); } }; var obj = Object.create(serializable, { propiedad: { enumerable: true, value: "valor" } }); Q.ninvoke(client, "set", "miClave", obj) .then(function() { return Q.ninvoke(client, "get", "miClave"); }) .then(function(value) { console.log("Valor: ", value); // Valor: {"propiedad": "valor"} }) .done();
  • 320. Redis = Strings!! var serializable = { toString: function() { return JSON.stringify(this); } }; var obj = Object.create(serializable); obj.propiedad = "valor"; Q.ninvoke(client, "set", "miClave", obj) .then(function() { return Q.ninvoke(client, "get", "miClave"); }) .then(function(value) { console.log("Valor: ", value); // Valor: {"propiedad": "valor"} }) .done();
  • 321. Redis = Strings!! Una solución más radical: Object.prototype.toString = function() { return JSON.stringify(this); }; var obj = {propiedad: "valor"}; Q.ninvoke(client, "set", "miClave", obj) .then(function() { return Q.ninvoke(client, "get", "miClave"); }) .then(function(value) { console.log("Valor: ", value); // Valor: {"propiedad": "valor"} }) .done();
  • 322. DEL / EXISTS / TYPE / RENAME Operaciones con claves • DEL: borrar una clave • EXSITS: comprobar si una clave existe • TYPE: el tipo de valor almacenado en una clave • RENAME: cambiar el nombre de la calve
  • 323. DEL / EXISTS / TYPE / RENAME var redis = require("redis"), Q = require("q"), client = redis.createClient(); Q.ninvoke(client, "exists", "MiClave") .then(function(exists) { console.log( exists? "Existe!" : "No existe..." ); return Q.ninvoke(client, "set", "MiClave", "MiValor"); }) .then(function() { return Q.ninvoke(client, "rename", "MiClave", "MyKey"); }) .then(function() { return Q.ninvoke(client, "type", "MyKey"); }) .then(function(type) { console.log("MyKey es de tipo", type); }) .done();
  • 324. Operaciones con cadenas • APPEND: añade el valor a la cadena • DECR/INCR: Decrementa/incrementa el valor en 1 • DECRBY/INCRBY: Dec/inc el valor en N • GETSET: Modifica el valor y devuelve el viejo • STRLEN: Longitud del valor
  • 325. Operaciones con cadenas var op = Q.ninvoke.bind(Q, client); op("set", "miClave", 1) .then(function() { return op("incrby", "miClave", 10); }).then(function() { return op("decr", "miClave"); }).then(function() { return op("getset", "miClave", "fin"); }).then(function(valor) { console.log("VALOR: ", valor); return op("strlen", "miClave") }).then(function(len) { console.log("Len: ", len); }) .done();
  • 326. Operaciones múltiples • MGET: Trae el valor de varias claves • MSET: Modifica el valor de varias claves var op = Q.ninvoke.bind(Q, client); op("mset", "miClave", 1, "otraClave", 2) .then(function() { return op("mget", "miClave", "otraClave"); }) .then(function(values) { console.log(values[0], ",", values[1]); }) .done()
  • 327. Listas • LPUSH/RPUSH key value [value ...] • LPOP/RPOP key • LINDEX key index • LSET key index value • LLEN key • LRANGE key start stop: trae el rango de elementos • LTRIM key start stop: limita al rango start-stop • RPOPLPUSH source dest: RPOP sour + LPUSH dest
  • 328. Listas var op = Q.ninvoke.bind(Q, client), makeOp = function() { var args = arguments; return function() { return op.apply(null, args); } }; op("del", "miClave") .then(makeOp("rpush", "miClave", 1, 2, 3, 4)) .then(makeOp("lrange", "miClave", 0, 2)) .then(function(values) { console.log(values); return op("ltrim", "miClave", 0, 1); }) .then(makeOp("llen", "miClave")) .then(function(len) { console.log(len); }) .done()
  • 329. A teclear un poco! Modifica el ejercicio del blog del tema anterior... • Para que utilice Redis como BD • Guardar los posts como objetos JSON • Guarda los usuarios en claves tipo: - “user:admin@asdf.com”: <JSON del usuario> • Modifica la estrategia del autenticación
  • 330. Hashes • HSET key field value: Modifica el valor de campo field del hash en value • HGET key field: Consulta el valor de campo field del hash en value • HEXISTS key field: Existe el campo field? • HKEYS/HVALS key: Todos los campos/valores • HGETALL: Trae el hash entero • HINCRBY key field n: Incrementa el campo en n • HMGET/HMSET: Operaciones múltiples
  • 331. Hashes op("del", "miClave").then(function() { return op("hmset", "miClave", "a", 1, "b", 2, "c", 3); }) .then(function() { return op("hincrby", "miClave", "c", 100); }) .then(function() { return op("hgetall", "miClave"); }) .then(function(hash) { console.log(hash); // { a: '1', b: '2', c: '103' } }) .done()
  • 332. Conjuntos • SADD key member [member ...]: añadir miembros • SREM key member [member ...]: quitar miembros • SCARD key: cardinal (número de elementos) • SDIFF key [key ...] • SINTER key [key ...] • SUNION key [key ...] • SISMEMBER • SMEMBERS key member: ¿es miembro? key: todos los miembros
  • 333. Conjuntos var op = Q.ninvoke.bind(Q, client), makeOp = function() { var args = arguments; return function() { return op.apply(null, args); } }; op("sadd", "miConjunto", 1, 1, 2, 3, 5, 8, 13) .then(makeOp("sadd", "miConjunto2", 1, 3, 5, 7, 9, 11, 13)) .then(makeOp("sinter", "miConjunto", "miConjunto2")) .then(function(values) { console.log("Intersección:", values); return op("sdiff", "miConjunto", "miConjunto2"); }) .then(function(values) { console.log("Diferencia:", values); }) .done()
  • 334. Ejercicio: acotador de URLs Escribe un acortador de URLs con Redis • Registro y login de usuarios utilizando simpleAuth • Redirección automática de urls • Estadísticas de visita - Cada IP cuenta una sola vez - ¿Estadísticas por fecha? ¿Tendencias? - ¿Geotracking de visitas (freegeoip.net)? http.get("http://freegeoip.net/json/83.44.23.171", function(res) { res.on("data", function(data) { console.log(JSON.parse(data)); }); });
  • 336. ¿Qué es MongoDB? Una base de datos NoSQL... • Sin esquema • Alto rendimiento • Almacena documentos BSON • Enfocada en escalabilidad horizontal • Lenguaje de consultas potente • Sin transacciones • Agregado de datos
  • 337. ¿Qué es MongoDB? Una base de datos de documentos • No impone forma a los datos • No necesita migraciones/reestructuración de la BBDD • Permite estructuras muy complejas • Herramientas potentes de agregado con JavaScript - Map-Reduce - Aggregation Pipeline
  • 338. ¿Qué es MongoDB? Con algunos extras interesantes... • Índices geoespaciales • Búsquedas FTS • Almacenamiento eficiente de blobs y ficheros • Sharding automático • Replicación
  • 339. ¿Qué es MongoDB? Es una buena alternativa para... ¡muchas cosas! • Prototipos y aplicaciones simples • Hacer la transición de front a back • Aplicaciones con mucha carga de escritura • Agregado de datos a un nivel medio/alto • Aplicaciones con datos muy heterogéneos • Enormes colecciones de datos (sharding) • Almacenar ficheros (sharding)
  • 340. ¿Qué NO es MongoDB? No te dejes seducir demasiado: • Mongo no puede hacer JOINs! • El lenguaje de consulta menos potente que SQL • No tiene transacciones! • La velocidad baja al subir la seguridad (escritura) • Ten cuidado: - Es muy fácil empezar con MongoDB - Si tu app crece mucho... vas a necesitar JOINs
  • 341. La idea general Un almacen de documentos • Básicamente, objetos JSON (BSON) • Dividido en colecciones • Consultas basadas en la estructura del documento • ¡Se integra genial con JavaScript!
  • 342. Requisitos Necesitas • Instalar y arrancar mongod ‣ mongod --dbpath <path> --nojournal • Tener la documentación a mano ➡ http://docs.mongodb.org/manual/ ➡ http://mongodb.github.io/node-mongodb-native/ • npm install • En c9.io mongodb •<comandos para instalar mongodb>
  • 343. Primeros pasos Para conectarte al servidor var MongoClient = require("mongodb").MongoClient , ObjectID = require("mongodb").ObjectID; var client = Q.ninvoke(MongoClient, "connect", "mongodb://127.0.0.1:27017/dbname"); client.fail(function(e) { console.log("ERROR conectando a Mongo: ", e) });
  • 344. Primeros pasos La BD está dividida en colecciones. Para abrir una colección: var collection = client.then(function(db) { return db.collection("coleccion"); });
  • 345. Documento Un documento es un objeto BSON { "_id" : ObjectId("524872a99c50880000000001"), "email" : "test@asdf.com", "password" : "asdf1234", "name" : "Test User", "date" : 1380479657300, "token" : "hm6ly43v.0o1or" }
  • 346. Documento Un documento es un objeto BSON { "_id" : ObjectId("524872a99c50880000000001"), "email" : "test@asdf.com", "password" : "asdf1234", "name" : "Test User", "date" : 1380479657300, "token" : "hm6ly43v.0o1or" }
  • 347. Documento Un documento puede contener arrays y otros documentos { "_id" : ObjectId("5249a2e9b90687d56453b2f3"), "text" : "Soy un comentario", "user" : { "_id" : ObjectId("524872a99c50880000000001"), "nombre" : "Test User", "avatar" : "/img/as09a8sd09.jpg" }, "tags" : [ "test", "prueba" ] }
  • 348. Documento Un documento puede contener arrays y otros documentos { "_id" : ObjectId("5249a2e9b90687d56453b2f3"), "text" : "Soy un comentario", "user" : { "_id" : ObjectId("524872a99c50880000000001"), "nombre" : "Test User", "avatar" : "/img/as09a8sd09.jpg" }, "tags" : [ "test", "prueba" ] }
  • 349. Documento Un documento puede contener arrays y otros documentos { "_id" : ObjectId("5249a2e9b90687d56453b2f3"), "text" : "Soy un comentario", "user" : { "_id" : ObjectId("524872a99c50880000000001"), "nombre" : "Test User", "avatar" : "/img/as09a8sd09.jpg" }, "tags" : [ "test", "prueba" ] }
  • 350. Documentos MongoDB no puede hacer JOINs • Sin embargo, se pueden empotrar documentos y arrays • Profundidad y complejidad arbitraria • El límite: documento < 16MB • Se suelen favorecer los diseños desnormalizados ✓ Mucho más cómodos ✓ Más eficientes en Mongo (con cuidado) ๏ Redundancia... ๏ Posible inconsistencia ๏ Actualizar los datos a mano cuando cambian
  • 351. Colecciones Una colección es una agrupación de documentos • Puede alojar cualquier documento (no impone estructura) • Puede alojar documentos con diferentes formas • Operaciones de consulta • Es donde se ponen los índices
  • 352. Colecciones Operaciones sobre una colección: • collection.save: guardar/actualizar un documento • collection.insert: inserta un documento • collection.findOne: recuperar un documento • collection.find: recuperar varios documentos • collection.remove: borrar uno o varios documentos • collection.drop: elimina la colección • collection.rename: cambia de nombre la colección • collection.count: número de documentos
  • 353. Colecciones MongoDB trae un cliente de línea de comandos • mongo <host>/<dbname> • Ejecuta JavaScript • Muy práctico para explorar
  • 356. Colecciones Desde Node.js var MongoClient = require("mongodb").MongoClient , ObjectID = require("mongodb").ObjectID , Q = require("q"); Q.ninvoke(MongoClient, "connect", "mongodb://127.0.0.1:27017/dbname") .then(function(db) { var micoleccion = db.collection("micoleccion"), op = Q.ninvoke.bind(Q, micoleccion); op("insert", {uno: 1, dos: 2}) .then(function() { return op("insert", {tres: 3, cuatro: [4]}); }) .then(function() { return op("findOne", {uno: 1}); }) .then(function(doc) { console.log(doc); }) .done(); });
  • 357. Consulta Dos operaciones fundamentales: •findOne: devuelve un documento •find: devuelve varios documentos en un cursor
  • 358. Consulta Ambos reciben como parámetro las conditiones que tiene que cumplir el documento: var micoleccion = db.collection("micoleccion"), op = Q.ninvoke.bind(Q, micoleccion); Q.ninvoke(micoleccion, "findOne", {uno: 1}) .then(function(doc) { console.log(doc); }); Q.ninvoke(micoleccion, "findOne", {dos: {$gt: 0}}) .then(function(doc) { console.log(doc); });