Sunteți pe pagina 1din 397

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 librera?

Qu tiene que ver v8 con Node.js?

Para qu sirve (adems 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...
Podramos decir que Node.js es el equivalente para
JavaScript?
Qu es Node.js?
NO!!
Qu es Node.js?
Qu es un lenguaje de programacin?
Qu es Node.js?
Qu es un lenguaje de programacin?
Una gramtica que dene la sintaxis del lenguaje
Un intrprete/compilador que lo sabe interpretar y
ejecutar
Qu es Node.js?
Qu es un lenguaje de programacin?
Una gramtica que dene la sintaxis del lenguaje
Un intrprete/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 programacin?
Una gramtica que dene la sintaxis del lenguaje
Un intrprete/compilador que lo sabe interpretar y
ejecutar
Mecanismos para interactuar con el mundo exterior
(llamadas al sistema)
Librera estndar (consola, cheros, red, etc,...)
Qu es Node.js?
Qu es un lenguaje de programacin?
Una gramtica que dene la sintaxis del lenguaje
Un intrprete/compilador que lo sabe interpretar y
ejecutar
Mecanismos para interactuar con el mundo exterior
(llamadas al sistema)
Librera estndar (consola, cheros, red, etc,...)
Utilidades (intrprete interactivo, depurador,
paquetes)
Qu es Node.js?
Una gramtica que dene la sintaxis del lenguaje
Un intrprete/compilador que lo sabe interpretar y
ejecutar
Mecanismos para interactuar con el mundo exterior
(llamadas al sistema)
Librera estndar (consola, cheros, red, etc,...)
Utilidades (intrprete interactivo, depurador, paquetes)
v8 (JavaScript)
Node.js
Qu es Node.js?

Node.js Ruby Python Java
Lenguaje
Motor
Entorno
Framework
JavaScript Ruby Python Java
v8 YARV cPython JavaVM
Node.js Ruby
Standard
Library
Python
Standard
Library
Java SE
??? Rails Django Spring
Qu es Node.js?
Node.js es algo ms:
Una losofa sobre cmo hacer las cosas
Un modelo de ejecucin singular
Muy enfocado hacia aplicaciones de red
Qu es Node.js?
Node.js es algo ms:
Una losofa sobre cmo hacer las cosas
Un modelo de ejecucin singular
Muy enfocado hacia aplicaciones de red
Una filosofa
Node.js se crea con un objetivo en mente:
Escribir aplicaciones muy ecientes (E/S) con el
lenguaje dinmico ms rpido (v8) para soportar miles
de conexiones simultneas
Sin complicarse la vida innecesariamente
-
Sin paralelismo
-
Lenguaje sencillo y muy extendido
-
API muy pequea y muy consistente
-
Apoyndose en Eventos y Callbacks
Una filosofa
Una filosofa
No es la mejor opcin 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 ests haciendo
Flexibilidad > magia
-
Tu cdigo es tu responsabilidad
-
Cada aplicacin es un mundo
Qu es Node.js?
Node.js es algo ms:
Una losofa sobre cmo hacer las cosas
Un modelo de ejecucin singular
Muy enfocado hacia aplicaciones de red
Un modelo de ejecucin
Para entender Node.js, tenemos que entender dos
ideas fundamentales:
Concurrencia vs. paralelismo (asincrona)
Eventos
Un modelo de ejecucin
Concurrencia vs. Paralelismo
Qu signica que dos cosas suceden a la vez?
Un modelo de ejecucin
Qu signica que dos cosas suceden a la vez?
Un modelo de ejecucin
Qu signica que dos cosas suceden a la vez?
Un modelo de ejecucin
Concurrencia vs. Paralelismo
Paralelismo: varios actores realizando una accin cada
uno simultneamente
Concurrencia: un solo actor, con varias tareas activas
entre las que va alternando
Un modelo de ejecucin
Paralelismo
Concurrencia
Un modelo de ejecucin
Un modelo de ejecucin
La potencia de Node.js es (curiosamente):
Un modelo de ejecucin concurrente
-
Muchos clientes o tareas activas
Pero NO paralelo
-
Una nica hebra
Un modelo de ejecucin

Qu ventajas tiene evitar el paralelismo?

Qu desventajas?

Y por tanto, Cundo es til el modelo de Node.js?


Un modelo de ejecucin
Patrn Reactor
Un patrn de diseo para manejar eventos donde peticiones
de servicio se transladan concurrentemente a un
manejador central que se encarga de desmultiplexar las
peticiones y despacharlas sncronamente mediante sus
manejadores particulares asociados.
Un modelo de ejecucin
Patrn Reactor
Un patrn de diseo para manejar eventos donde peticiones
de servicio se transladan concurrentemente a un
manejador central que se encarga de desmultiplexar las
peticiones y despacharlas sncronamente mediante sus
manejadores particulares asociados.
WAT??
Un modelo de ejecucin
Patrn Reactor
Bucle Principal
Un modelo de ejecucin
Patrn Reactor
Bucle Principal
Mundo Exterior
Un modelo de ejecucin
Patrn Reactor
Bucle Principal
Mundo Exterior
Suceso
Patrn Reactor
Un modelo de ejecucin
Bucle Principal
Mundo Exterior
Suceso
Patrn Reactor
Un modelo de ejecucin
Bucle Principal
Mundo Exterior
Suceso
Suceso 2
Patrn Reactor
Un modelo de ejecucin
Bucle Principal
Mundo Exterior
Suceso
Suceso 2
Patrn Reactor
Un modelo de ejecucin
Bucle Principal
Mundo Exterior
Suceso
Suceso 2
Tick!
Patrn Reactor
Un modelo de ejecucin
Bucle Principal
Mundo Exterior
Suceso
Suceso 2
Tick!
Evento:
Suceso
Patrn Reactor
Un modelo de ejecucin
Bucle Principal
Mundo Exterior
Suceso
Suceso 2
Tick!
Evento:
Suceso
Manejadores
Patrn Reactor
Un modelo de ejecucin
Bucle Principal
Mundo Exterior
Suceso
Suceso 2
Tick!
Manejadores
Patrn Reactor
Un modelo de ejecucin
Bucle Principal
Mundo Exterior
Suceso
Suceso 2
Tick!
Manejadores
Suceso 3
Patrn Reactor
Un modelo de ejecucin
Bucle Principal
Mundo Exterior
Suceso
Suceso 2
Tick!
Manejadores
Suceso 3
Patrn Reactor
Un modelo de ejecucin
Bucle Principal
Mundo Exterior
Suceso
Suceso 2
Tick!
Manejadores
Suceso 3
Listo!
Patrn Reactor
Un modelo de ejecucin
Bucle Principal
Mundo Exterior
Suceso
Suceso 2
Tick!
Manejadores
Suceso 3
Patrn Reactor
Un modelo de ejecucin
Bucle Principal
Mundo Exterior
Suceso
Suceso 2
Tick!
Manejadores
Suceso 3
Evento:
Suceso 2
???
Patrn Reactor
Un modelo de ejecucin
Bucle Principal
Mundo Exterior
Suceso
Suceso 2
Manejadores
Suceso 3
Patrn Reactor
Programacin contra eventos
Una vez puestos los manejadores, se pueden ejecutar
en cualquier orden
-
El orden lo determina el orden en que aparezcan sucesos
La ejecucin de los manejadores bloquea la hebra
Nunca hay dos manejadores ejecutndose al mismo
tiempo
Muy eciente... cuando E/S >> ejecuin del manejador
Un modelo de ejecucin
Qu es Node.js?
Node.js es algo ms:
Una losofa sobre cmo hacer las cosas
Un modelo de ejecucin 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 ms deprisa por dedicarle un camarero
slo 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)
-
Asegrate de tener tambin 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 patrn 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 cdigo del tirn
Coloca los manejadores que hayas denido
Si no hay ningn 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);
setInterval(function() {
console.log("Hola otra vez, Mundo del futuro!");
}, 1000);
Ahora en serio: Qu es Node.js?
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("Excepcin!");
}
Ahora en serio: Qu es Node.js?
Nos surgen problemas curiosos...

Excepciones?
try {
setTimeout(function() {
throw new Error("Pet!");
}, 0);
} catch(e) {
console.log("Excepcin!");
}
EventEmitter
Nuestro cdigo va a estar dirigido por eventos
Node.js tiene su propio estndar
Trae una implementacin del patrn Observador (o
Pub/Sub): EventEmitter
Todas sus libreras (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 mdulos (paquetes, otros cheros)
Garanta: una nica vez
Devuelve el mdulo!
require/exports
exports.propiedadPublica = <valor>
El otro lado del mecanismo
Se puede exportar cualquier valor
require/exports
codigo.js
libreria.js
var lib = require("./libreria");
console.log(lib.propiedad);
console.log("una vez");
exports.propiedad = "Pblica";
Para que te confes...
Haz un mdulo 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 confes...
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 (cheros, sockets, etc)
Descriptores de cheros
Puertos
...
JavaScript y el Universo
Tenemos que aadir nuevos conceptos a nuestro JS
Streams (lectura, escritura o duplex)
Buffers (representacin 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
Tamao jo
Manipular datos directamente
-
Sockets
-
Implementar protocolos complejos
-
Manipulacin de cheros/imgenes
-
Criptografa
-
...
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
var buf = new Buffer(100);
buf.write("abcd", 0, 4, "ascii");
console.log(buf.toString("ascii"));
Tamao del buffer
JavaScript y el Universo
Buffers
var buf = new Buffer(100);
buf.write("abcd", 0, 4, "ascii");
console.log(buf.toString("ascii"));
Datos
Posicin
Longitud
Codicacin
JavaScript y el Universo
Buffers
var buf = new Buffer(100);
buf.write("abcd", 0, 4, "ascii");
console.log(buf.toString("ascii"));
Codicacin
JavaScript y el Universo
Buffers
JavaScript y el Universo

function Bitmap(w, h) {
this.width = w;
this.height = h;
this.header = "P6\n" + w + " " + h + "\n255\n";
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 informacin
-
Lectura / Escritura / Duplex
Detrs de muchos mecanismos de Node.js
-
stdin/stdout
-
request HTTP
-
sockets
-
etc...
Instancias de EventEmitter
Acceso asncrono
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 ledo 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 ledo:", chunk.length);
});
readStream.on("end", function() {
console.log("ya est!");
});
Una fcil
Haz un programa que cuente las lneas de un chero
Una fcil
Haz un programa que cuente las lneas de un chero
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 vaco (puedes escribir ms)
- 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 + " lneas ms 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 n...)
HTTP
Node.js trae un servidor web estupendo
Asncrono
-
No bloquea la hebra
-
Cada cliente conectado consume muy poquitos recursos
-
Genial para miles de conexiones simultneas
Relativamente rpido
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);
HTTP

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

var http = require("http");
var server = http.createServer();
server.on("request", function(req, res) {
res.end("Hola, Mundo!");
});
server.listen(3000);
Respuesta
(stream)
Request
(objeto)
Eventos!
HTTP
El servidor HTTP
Eventos:
-
connection
-
request
Operaciones:
-
createServer([requestCallback])
-
listen(puerto, [hostname], [backlog], [callback])
-
close([callback])
HTTP
http.IncomingMessage (parametro req)
Representa la peticin HTTP del cliente
Propiedaes:
- req.headers: cabeceras de la peticin
- req.method: verbo HTTP
- req.url: url de la peticin
- 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]): cdigo HTTP y
cabeceras de la respuesta
- res.statusCode: [propiedad] Otra forma de establecer el
cdigo 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 ow de trabajo:
Editar, matar el proceso, volver a lanzarlo, probar... muy
tedioso!
Instlate nodemon
npm install -g nodemon
Reinicia el servidor cada vez que cambia el chero
$ nodemon <fichero.js>
HTTP
Node.js trae un mdulo 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 mdulo 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 ms difcil
Escribe un servidor de cheros
Lee el pathname de la URL
Busca un chero con esa ruta dentro de ./public
Si existe, lo sirve
Si no existe, devuelve 404
Un poco ms difcil: notas
fs.exists(filePath, callback)
Llama al callback con true si el chero lePath existe
O con false si no existe
var fs = require("fs");
fs.exists("./hola.txt", function(exists) {
console.log(exists);
});
Un poco ms difcil: notas
fs.readFile(filePath, callback)
Otra manera de leer cheros
Intenta leer lePath e invoca a callback con dos
parmetros:
- err: null si todo ha ido bien o, si hubo error, el error
- data: todo el contenido del chero (si fue posible leerlo)
var fs = require("fs");
fs.readFile("./hola.txt", function(err, data) {
if (err) { /* error! */ }
console.log(data);
});
Un poco ms difcil: eplogo
Cmo podramos aadir un cach para no leer los
cheros del disco duro ms de una vez?
Cmo podramos hacer que los cheros cacheados
se liberaran despus de x minutos?
Cmo podramos escribir un registro de acceso?
Un poco ms difcil: variaciones
Haz un contador de aperturas de emails
Sirviendo un .gif de 1x1 y contando cuantas veces lo
sirves
Mejor an, cuenta solo a cuntas IPs lo sirves
Haz un servicio de avatares que cambie segn:
La hora del da
La frecuencia con que es pedido (popularidad)
Un poco ms difcil: 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
peticin 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 segn el caso asignado
y se sirve
Si pide success.png, se marca su caso como exitoso y
se le sirve la imgen
/stats devuelve un JSON con info sobre los casos
(visitas totales y visitas exitosas por cada caso)
Servidor A/B Testing
Tenis maqueta y recursos en :
/tema1/abtesting
Promesas
Node.js y CPS
Node.js maneja la asincrona utilizando callbacks
Continuation Passing Style
Continuaciones explcitas como funciones
Cuando termines, ejecuta esta otra funcin
Node.js y CPS
Los callbacks tienen muchas ventajas
Muy fciles de entender e implementar
Familiares para el programador JavaScript
Extremadamente exibles (clausuras, funciones de
primer orden, etc, ...)
Un mecanismo universal de asincrona/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
!a"back He"
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 asincrona
Construccin explcita del ujo de ejecucin
Separacin en bloques consecutivos
Manejo de errores ms controlado
Combinacin de diferentes ujos asncronos
Promesas
Una promesa = Un ujo de ejecucin
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 ujo de ejecucin
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 ujo de ejecucin
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 ujo de ejecucin
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 ujo de ejecucin
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 ujo de ejecucin
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 ujo de ejecucin
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 ujo de ejecucin
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 ujo de ejecucin
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 ujo de ejecucin
Promesas
Pero an no hemos ejecutado nada! Solamente
hemos construdo el ujo
Promesas
Ventajas?
Cdigo mucho ms ordenado y ms legible
Mejor control de errores
Podemos manipular el ujo
-
Aadir nuevas etapas
-
Devolverlo en funciones
-
Pasarlo como parmetro
Podemos combinar varios ujos
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)...
-
Slo se ejecuta si el anterior se ha ejecutado sin errores
-
Recibe como parmetro 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 parmetro 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 data
1 2
Promesas

data data
1 2
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!");
});
Promesas

data data
1
2
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!");
});
2
Promesas

data data
1
2
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...");
});
2
Promesas: walled garden
Vamos a empezar a trastear con promesas...
Pero, de momento, con una librera 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

Qu se muestra por consola al ejecutar esto?
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

Por qu?
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
Una promesa = un ujo de ejecucin
Conguramos un rbol de ujos e ejecucin
Aadimos bloques a promesas
Pero... Cundo se empieza a ejecutar?
Promesas: walled garden
Una promesa = un ujo de ejecucin
Conguramos un rbol de ujos e ejecucin
Aadimos bloques a promesas
Pero... Cundo 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 ejecucin 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
excepcin 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
Propagacin 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
Propagacin 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
Propagacin 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
Propagacin 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 revs?
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 estn divididas en dos objetos:
La promesa en s
-
Interfaz limitada a la construccin de ujos
-
.then, .fail y algn mtodo ms
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 ujo que necesita
Promesas
Un ejemplo realista: readFilePromise(file)
Implementa la funcin
Basndote en fs.readFile()
Devuelve una promesa
Se resuelve con el contenido del chero
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");
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);
});
Gestor del recurso
Consumidor del recurso
Promesas: parntesis
Intenta modicar el servidor de cheros estticos
para que utilice promesas:
leExistsPromise
readFilePromise
Promesas: combinaciones
Q es una librera muy potente
Trae un montn de funcionalidad
Muy recomendable leer la documentacin
Nosotros vamos a ver dos cosas esenciales:
-
Combinacin 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
tambin 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 parmetros
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 automticamente 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 automticamente 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); // resolucin 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 ujo, 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, djala abierta
Si eres el consumidor nal de la promesa, asegrate de
cerrarla con .done()
A teclear!
Vamos el primer ejercicio complicadillo: un servidor
de cheros versionado
La herramienta que hemos estado utilizando para
compartir cdigo
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, ruta): ruta absoluta a ruta
relativa (desde base)
Necesitas saber...
fs.readdir(ruta): Listar cheros de un directorio
Devuelve un array de strings con los nombres de los
cheros
No es recursivo
No hay manera de saber si una entrada es un chero 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 chero/directorio
stats.isDirectory(): true si es un directorio
stats.mtime: Date de la ltima modicacin
var fs = require("fs"),
Q = require("q");
Q.ninvoke(fs, "stat", ".").then(function(stats) {
console.log("es dir?", stats.isDirectory());
console.log("ltima modificacin:", stats.mtime);
})
Primer paso: listado recursivo
Escribe una funcin listAllFiles(ruta) que:
Devuelva una promesa
La promesa se resuelva con un listado recursivo de
todos los cheros que hay dentro del directorio
Para cada chero, 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
Funcin somethingChanged(ruta):
Devuelve true si algn chero ha sido modicado desde
la ltima vez que se invoc
Implemntalo utilizando stats.mtime como referencia
Utilizando esa funcin, escribe un demonio que
monitorize un directorio y escriba un mensaje por
consola cada vez que hay cambios
Tercer paso: volcado a memoria
Funcin readAllFiles(ruta):
Completa el resultado de listAllFiles() aadiendo
una tercera propiedad contents con los contenidos
del chero
Haz que el demonio lea todos los cheros si detecta
algn 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 cheros de la versin n
/ruta/al/fichero?version=<n>: busca el chero con
la ruta correspondiente en la versin n y lo sirve
la versin latest siempre apunta a la versin ms
reciente
Virgueras opcionanes
Escribe un mdulo simpleRoute de modo que
podamos denir rutas as:
var routes = require("./simpleRoute");
routes.get("/", function(req, res) {
})
routes.get("/list", function(req, res) {
})
routes.default(function(req, res) {
})
Virgueras opcionales
Adems, simpleRoute modica el parmetro
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 exible
Muy popular
Se adapta muy bien a la losofa de Node
Similar a Sinatra, Sylex, Flask, Spark, ...
Qu es express?
Express nos va ayudar con...
Rutas
Parmetros
Formularios y subida de cheros
Cookies
Sesiones
Templates
Qu es express?
Express NO nos va ayudar con...
Base de datos / ORM
Autenticacin de usuarios
Seguridad
Migraciones
Deployment
Organizacin del cdigo
Qu es express?
Ms concretamente, express...
Construye sobre http
Procesando la peticin por un stack de middleware que
se encarga de decorar las peticiones
-
Asocia rutas a manejadores
-
Decorar los objetos req y res (parseo de parmetros,
multipart, etc,...)
-
Rendear templates
Nosotros escogemos qu middlewares queremos usar,
y en qu orden
Qu es express?

var express = require("express");
var app = express();
// configuracin + rutas
app.listen(3000);
Qu es express?
Equivalente a:
var express = require("express"),
http = require("http");
var app = express();
// configuracin + rutas
http.createServer(app).listen(3000);
Qu es express?
Equivalente a:
var express = require("express"),
http = require("http");
var app = express();
// configuracin + 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?

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

var app = require("express")();
app.get("/", function(req, res) {
res.end("Hola desde express!");
});
app.listen(3000);
Objeto
Stream
Qu es express?
Qu nos aporta express, exactamente?
Depende de los middlewares que usemos!
Algunas cosas vienen por defecto
Request
req.params: parmetros 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])
Modica la cookie nombre con el valor valor
res.cookie("visitas", "1", {domain: ".ejemplo.com"});
Response
res.redirect([status], url)
Redirige a url
El cdigo de estado es opcional (302 por defecto)
res.redirect(301, "http://www.google.com");
Response
res.send([status], body)
Enva una respuesta (escribe en el buffer)
Lo ms 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 mtodos auxiliares:
Cabeceras
Envo de cheros
JSONP
Content-Type
Lee la documentacin!
http://expressjs.com/api.html
Application
app.configure([entorno], callback)
Congurar la aplicacin
Opcionalmente: conguracin para un entorno
-
development
-
testing
-
production
-
etc
app.configure('development', function(){
app.set('db uri', 'localhost/dev');
})
Application
Qu signica congurar la aplicacin?
Crear algunas propiedades globales
Especicar el stack de middleware
Application
app.set(prop, value) / app.get(prop)
Escribe/consulta valores globales en app
Bsicamente intil...
Excepto para cambiar alguna conguracin ms
avanzada
app.set('title', 'Redradix');
app.get('title');
Middleware
Middleware son mdulos plug and play que se
pueden apilar arbitrariamente en cualquier orden
y proveen cierta funcionalidad
Filtros: procesan trco entrate/saliente, pero no
responden a ninguna request. (ejemplo: bodyParser)
Proveedores: ofrecen respuestas automticas a algn
tipo de peticin (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 mdulos de terceros
https://github.com/senchalabs/connect/wiki
express.favicon(ruta)
Sirve el favicon de la aplicacin
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 peticin
Opcional: rmar 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
Decodica
-
application/json
-
application/x-www-form-urlencoded
-
multipart/form-data
Crea el objeto req.body con los parmetros POST
Crea el objeto req.files con los cheros que se han
subido desde un formulario
express.cookieSession([opciones])
Inicializa y parsea los datos de sesin del usuario
Crea el objeto req.session
Utilizando cookies como almacenamiento
Opciones:
-
secret: rma de segurdad para la cookie
-
maxAge: duracin, en ms (default: sin caducidad)
-
path: ruta para la que es vlida 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 cheros estticos dentro de dir
Muy til! Se pone cerca del nal
Cachea los cheros
La variable global __dirname contiene el directorio
donde reside el script en ejecucin
app.router
El enrutado de la aplicacin
Sirve para especcar exctamente 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
Agnstico
Modular
Simple
NO trae ningn 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 sera 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 prctica lo que hemos visto

Vamos a hacer un blog, simplicado

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 parmetros 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);
});
Ms Middleware
Escribir middleware para express es muy sencillo:
Una funcin que recibe tres parmetros:
-
req
-
res
-
next
Al terminar su tarea, tiene que invocar a next()
-
Sin parmetro: se invoca al siguiente middleware del stack
-
Con parmetro: se cambia la ruta a lo que se pase como
parmetro
Ms 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();
});
})
Ms Middleware
Haz un logger que muestre:
Hora de la peticin
IP
Mtodo
Ruta
Tiempo de respuesta de la peticin
Pero solo si el tiempo de respuesta est muy por
encima de la media!
Ms Middleware
Haz un mdulo de mensajes ash
Mensajes que solo estn disponibles para la siguiente
request
Muy tiles para informar de ciertos sucesos
-
Post creado con xito
-
Email enviado
-
Errores
-
etc...
Ms 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());
})
Ms 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());
})
Ms 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());
})
Ms Middleware: Errores
Un caso especial de middleware: una funcin que
reciba un parmetro ms
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)
Ms Middleware: Errores
Ms 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)
Ms Middleware: Errores
Escribe un manejador de errores para el blog
Pgina 404 (pgina no encontrada o ruta no existe)
Pgina 500 (mostrar en caso de error/excepcin)
Registra la fecha, la ruta y el mensaje de error en
errors.log
Ms Middleware
Dos maneras de activar middlewares
Globalmente (app.use), activos para toda la app
Locales para ciertas rutas
Ms 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);
Ms Middleware
Anar la funcionalidad de cada ruta
Pre-procesado de URL (parmetros, formato)
Seguridad
Cach
Mtricas
Ms Middleware
Escribe un mdulo para cachear la respuesta de una
ruta durante X ms
Duracin en ms
app.get("/", new StaticCache(5*1000), function(req, res) {
res.send("Envo mi respuesta..." + new Date());
})
Ms Middleware
Una variacin:
Cachea a un chero
Sustituye res.write por el stream del chero
Cuando se dispare finish, sirve lo que has
almacenado
Puedes aadirlo al ejercicio del Blog para cachear las
pginas de detalle de un post
SimpleAuth
Mdulo para autenticar usuarios simpleAuth.js
Sencillo y exible
Independiente de la BBDD
Utilizando las sesiones de express
Middleware de ruta
SimpleAuth
El mdulo provee 3 middlewares:
createSession: comprueba los credenciales de usuario
y crea una nueva sesin 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 denir una estrategia:
serializeUser: Cmo serializar un usuario?
deserializeUser: Cmo 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 funcin para usar como middleware
SimpleAuth
auth.createSession(config)
Genera una funcin que...
1. Extrae username y password segn cong de req.body
2. Llama a strategy.checkCredentials con username y
password
3. Si son credenciales correctos:
3.1. Crea una entrada en la sessin 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
sesin 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 sesin o cookie
SimpleAuth
auth.destroySession
1. Borra la entrada adecuada en la sesin o cookie
SimpleAuth
auth.setStrategy(strategy)
1. Congura la estretagia:
-
strategy.serializeUser
-
strategy.deserializeUser
-
strategy.checkCredentials
-
strategy.loginRoute
SimpleAuth
Protege la edicin del blog con simpleAuth
Utilizando un array de usuarios escrito directamente en
el cdigo
Pgina 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 rpida
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 rpidas
Muy fcil de aprender, muy fcil de usar mal
Qu es Redis?
Redis es una opcin estupenda para...
Datos de acceso inmediato
-
Sesiones
-
Cachs
Escrituras muy rpidas
-
Logs
-
Conteos y estadsticas
Canal de comunicacin pub/sub
-
Comunicacin entre procesos (workers, big data)
-
Chats :)
Qu NO es Redis?
Redis es una opcin terrible para...
Relaciones de datos complejas
Bsquedas
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
-
Cachs
-
Estadsticas
-
Workers
-
Sesiones
-
Registros
-
Colas
La idea general
Redis es un gran Hash de claves
No hay concepto de tabla/coleccin
El valor fundamental es la cadena (string)
Pero una clave puede contener un valor ms 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 documentacin
!
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)
client.nombreDelComando(arg1, arg2, callback)
callback: function(err, 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"),
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 solucin ms 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: aade el valor a la cadena
DECR/INCR: Decrementa/incrementa el valor en 1
DECRBY/INCRBY: Dec/inc el valor en N
GETSET: Modica 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 mltiples

MGET: Trae el valor de varias claves

MSET: Modica 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!
Modica 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>
Modica la estrategia del autenticacin
Hashes
HSET key field value: Modica el valor de campo
eld del hash en value
HGET key field: Consulta el valor de campo eld
del hash en value
HEXISTS key field: Existe el campo eld?
HKEYS/HVALS key: Todos los campos/valores
HGETALL: Trae el hash entero
HINCRBY key field n: Incrementa el campo en n
HMGET/HMSET: Operaciones mltiples
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 ...]: aadir miembros
SREM key member [member ...]: quitar miembros
SCARD key: cardinal (nmero de elementos)
SDIFF key [key ...]
SINTER key [key ...]
SUNION key [key ...]
SISMEMBER key member: es miembro?
SMEMBERS 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("Interseccin:", 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
Redireccin automtica de urls
Estadsticas de visita
-
Cada IP cuenta una sola vez
-
Estadsticas 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/reestructuracin 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
Bsquedas FTS
Almacenamiento eciente de blobs y cheros
Sharding automtico
Replicacin
Qu es MongoDB?
Es una buena alternativa para... muchas cosas!
Prototipos y aplicaciones simples
Hacer la transicin de front a back
Aplicaciones con mucha carga de escritura
Agregado de datos a un nivel medio/alto
Aplicaciones con datos muy heterogneos
Enormes colecciones de datos (sharding)
Almacenar cheros (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 fcil empezar con MongoDB
-
Si tu app crece mucho... vas a necesitar JOINs
La idea general
Un almacen de documentos
Bsicamente, 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 documentacin a mano
!
http://docs.mongodb.org/manual/
!
http://mongodb.github.io/node-mongodb-native/
npm install mongodb
En c9.io
<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
coleccin:
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 lmite: documento < 16MB
Se suelen favorecer los diseos desnormalizados
!
Mucho ms cmodos
!
Ms ecientes en Mongo (con cuidado)
!
Redundancia...
!
Posible inconsistencia
!
Actualizar los datos a mano cuando cambian
Colecciones
Una coleccin es una agrupacin 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 coleccin:
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 coleccin
collection.rename: cambia de nombre la coleccin
collection.count: nmero de documentos
Colecciones
MongoDB trae un cliente de lnea de comandos
mongo <host>/<dbname>
Ejecuta JavaScript
Muy prctico 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 parmetro 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); });
Consulta
Los operadores de bsqueda:
$gt / $gte: mayor/mayor o igual
$lt / $lte: menor/menor o igual
$ne: diferente
$in / $nin: en/no en array de valores
micol.findOne({ valor: {$in: [ 5, 15 ] }}, cb)
Consulta
Los operadores lgicos:
$or: se cumple alguna clusula
$and: se cumplen todas las clusulas
$nor: el resultado opuesto
$not: no se cumplen todas las clusulas
micol.findOne({$or: [
{valor: 5},
{precio: {$gt: 15 }}
]}, callback)
Consulta
Ms operadores interesantes:
Operadores de evaluacin
-
Regex
-
Cdigo JavaScript arbitrario
Operaciones geoespaciales
! http://docs.mongodb.org/manual/reference/operator/nav-query/
Cursores
El operador find(...) devuelve un cursor
Representa un conjunto de resultados
cursor.count(callback): cantidad de documentos
cursor.limit(n): limitar a n documentos
cursor.skip(n): saltarse los n primeros documentos
cursor.nextObject(callback): siguiente documento
cursor.each(callback): para cada doc, en orden
cursor.toArray(callback): convierte el cursor en array
Cursores
cursor.sort(opciones, [callback])
Ordenar los resultados
Opciones del tipo:
"
[[campo, 1], [otroCampo, -1]]
" 1 para ascendente, -1 para descendente
coleccion.find()
.sort([['a', -1]])
.nextObject(function(err, item) {
// ...
})
Modificacin
El operador ms sencillo para modicar: save
Si el documento es nuevo (no tiene _id), lo inserta
Si el documento ya existe, lo modica
db.micol.save({ nombre: "Test User" })
Modificacin
insert(<documento o array>)
Inserta uno o varios documentos en la coleccin
db.micol.insert([
{ nombre: "Test User" },
{ nombre: "Test User 2" }
])
Escritura
remove(<patrn>)
Elimina los documentos que satisfagan la bsqueda
op("insert", {a: 1})
.then(function() {
return op("remove", {a: 1});
})
.then(function(doc) {
return op("count");
})
.then(function(n) {
console.log(n);
})
.done()
Ejercicio: Cln de Digg
Vamos a hacer un clon de Digg/HN/Reddit/...
El usuario se puede registrar/loguear
Puede aadir links (url+ttulo+descripcin)
Puede votar +1/-1 los links (solo una vez)
Puede comentar los links posteados
Puede votar +1/-1 los comentarios
Recientes/Populares
Ejercicio: Cln de Digg
La mecnica es ligeramente distinta al ej. anterior
El cliente es una Single Page App en Backbone que slo
hace peticiones a una API JSON (autenticadas por
token)
No hay vistas, solo respondemos con datos JSON
Algunas rutas tienen parmetros extra...
Ejercicio: Cln de Digg
Peculiaridades:
/me: informacin sobre el usuario logueado
/posts?s=<seccion>&page=<page>
- seccion == hottest: ordenados por voto
-
else: ordenados por fecha desc.
/posts/:postsid/vote/up
/posts/:postsid/vote/down
/comments/:commentsid/vote/up
/comments/:commentsid/vote/down
Ejercicio: Cln de Digg
Para loguearse:
El usuario manda user+pass a POST /session
El servidor:
1. Genera un token para el usuario
2. Lo guarda en user.token
3. Devuelve el JSON del usuario al cliente
Ejercicio: Cln de Digg
Datos del usuario
{
"email" : "asdf@asdf.com",
"name" : "Test User",
"date" : 1380479657300,
"_id" : ObjectId("524872a99c50880000000001"),
"token" : "hm6ly43v.0o1or"
}
Ejercicio: Cln de Digg
Datos del post
{
"_id" : ObjectId("524889c998ea730000000001"),
"date" : 1380485577004,
"description" : "asdf",
"link" : "http://www.google.com",
"ncomments" : 9,
"title" : "titulo",
"user" : {
"name" : "Test User",
"_id" : ObjectId("524872a99c50880000000001")
},
"votes" : 0
}
Ejercicio: Cln de Digg
Datos del comentario
{
"text" : "Comentario!",
"post_id" : ObjectId("524889c998ea730000000001"),
"user" : {
"name" : "Test User",
"_id" : ObjectId("524872a99c50880000000001")
},
"votes" : 0,
"date" : 1380485577013,
"_id" : ObjectId("524889c998ea730000000002")
}
Socket.io
Qu son websockets?
Protocolo de comunicacin
Full-duplex
Una sola conexin permanente
Stream de mensajes
Contenido en tiempo real
Qu son websockets?
Es decir...
El cliente puede enviar y recibir datos en tiempo real
Orientado a eventos (mensajes)
Siempre conectado
Baja latencia
Websockets y Node.js
Funcionan especialemente bien con Node.js
El servidor maneja muchas conexiones simultneas
Buena integracin con JSON
Eventos
Para qu sirven?
Fundamentalmente, para:
Actividades colaborativas
Juegos multijugador
Acelerar ciertas operaciones
Enviar datos
Cargar recursos
En resumen: tiempo real en vez de a peticin
Socket.io
Vamos a usar Socket.io
Una librera para manipular websockets
Muy popular
Fallback para navegadores obsoletos
Muy fcil de usar
!
http://socket.io/
Socket.io
Socket.io tiene dos partes:
Servidor (Node.js):
Cliente:
var express = require("express"),
server = require("http").createServer(),
io = require("socket.io").listen(server),
app = express();
server.on("request", app).listen(3000);
<script src="socket.io/socket.io.js"></script>
Socket.io
Los sockets emiten eventos
Un evento = un mensaje
Se pueden pasar parmetros
socket.on(mensaje, callback)
socket.emit(mensaje, [param1, param2, ...])
Socket.io
server.js
var express = require("express"),
app = express(),
server = require("http").createServer(app),
io = require("socket.io").listen(server);
app.use(express.static(__dirname + "/public"));
io.sockets.on("connection", function(socket) {
socket.emit("ping");
socket.on("pong", function() {
console.log("PONG!");
});
});
server.listen(3000);
Socket.io
server.js
var express = require("express"),
app = express(),
server = require("http").createServer(app),
io = require("socket.io").listen(server);
app.use(express.static(__dirname + "/public"));
io.sockets.on("connection", function(socket) {
socket.emit("ping");
socket.on("pong", function() {
console.log("PONG!");
});
});
server.listen(3000);
Socket.io
server.js
var express = require("express"),
app = express(),
server = require("http").createServer(app),
io = require("socket.io").listen(server);
app.use(express.static(__dirname + "/public"));
io.sockets.on("connection", function(socket) {
socket.emit("ping");
socket.on("pong", function() {
console.log("PONG!");
});
});
server.listen(3000);
Socket.io
index.html
<html>
<head>
<script src="/socket.io/socket.io.js"></script>
<script type="text/javascript">
var socket = io.connect("http://localhost:3000");
socket.on("ping", function() {
console.log("PING!");
socket.emit("pong");
});
</script>
</head>
<body></body>
</html>
Socket.io
index.html
<html>
<head>
<script src="/socket.io/socket.io.js"></script>
<script type="text/javascript">
var socket = io.connect("http://localhost:3000");
socket.on("ping", function() {
console.log("PING!");
socket.emit("pong");
});
</script>
</head>
<body></body>
</html>
Socket.io
index.html
<html>
<head>
<script src="/socket.io/socket.io.js"></script>
<script type="text/javascript">
var socket = io.connect("http://localhost:3000");
socket.on("ping", function() {
console.log("PING!");
socket.emit("pong");
});
</script>
</head>
<body></body>
</html>
Socket.io
Eventos reservados (servidor):
io.sockets.on(connection, cb)
socket.on(message, cb)
socket.on(disconnect, cb)
Cliente:
socket.on(connect, cb)
socket.on(disconnect, cb)
socket.on(error, cb)
socket.on(message, cb)
Socket.io
Mtodos (servidor)
socket.broadcast.emit(msg)
-
les llega a todos menos el emisor
socket.disconnect()
socket.emit(msg) / socket.on(msg)
Mtodos (client)
var socket = io.connect(host)
socket.disconnect()
socket.emit(msg) / socket.on(msg)
Un Chat! (simple)
Vamos a hacer un chat sencillo:
Los usuarios se loguean eligiendo un nick
Todo el mundo escribe en la misma sala comn
No tenemos indicador de presencia
Un Chat! (simple)
En el cliente:
Chat.registerHandler(cb): callback cuando el
usuario escribe
Chat.postMsg(user, msg): Muestra un mensaje de
otro
Chat.showMyMsg(user, msg): Muestra un mensaje
propio
Donde:
user: {avatar: <string>, name: <string>}
msg: {text: <string>, time: <date>}
Canales
Con Socket.io podemos crear canales o namespaces
para agrupar los receptores
var express = require("express"),
app = express(),
server = require("http").createServer(app),
io = require("socket.io").listen(server);
app.use(express.static(__dirname + "/public"));
io.of("/canal").on("connection", function(socket) {
socket.emit("ping");
});
server.listen(3000);
Canales
En el cliente:
<script src="/socket.io/socket.io.js"></script>
<script type="text/javascript">
var socket = io.connect("http://localhost:3000/canal");
socket.on("ping", function() {
console.log("PING!");
});
</script>
Canales
Podemos tener varios canales simultneos
(multiplexando el mismo websocket)
io.of("/canal").on("connection", function(socket) {
socket.emit("ping");
});
io.of("/otro").on("connection", function(socket) {
socket.emit("bang!");
});
Canales
En el cliente:
var canal = io.connect("http://localhost:3000/canal"),
otro = io.connect("http://localhost:3000/otro");
canal.on("ping", function() {
console.log("PING!");
});
otro.on("bang!", function() {
console.log("Estoy herido!");
});
Ahora, multisala
Utilizando namespaces, los usuarios pueden:
Loguearse/registrarse (simpleauth)
Crear salas
Unirse y salirse de las salas creadas
Escribir en la sala en la que estn
Ahora, multisala
Consejos:
Guarda los sockets de cada usuario en un objeto
Utiliza la sesin (o req.user) para saber en qu sala est
un usuario (clave del objeto de sockets + canal)
Crea mensajes para:
-
Un usuario ha entrado en la sala
-
Un usuario ha salido de la sala
-
Alguien postea un mensaje

S-ar putea să vă placă și