Cargando



Manejo de eventos en Node.js

En este tutorial veremos cómo es el manejo de eventos con Node.js utilizando el emisor de eventos junto a los listeners para construir aplicaciones tanto eficientes como flexibles.


may 29 2015 01:54
Profesional
ago 22 2016 15:54

En Node.js muchos de los objetos presentes en el entorno emiten eventos, como por ejemplo un servidor TCP emite un evento de tipo connect cada vez que un nuevo cliente se conecta o un flujo de archivos emite información cada vez que una porción de información es leída.

 

Esto en Node.js es lo que se les llama emisores de eventos, los cuales permiten a los desarrolladores la posibilidad de suscribir eventos, donde este suscribe una función callback que será invocada cada vez que un evento en el emisor de eventos ocurra. Incluso podemos crear nuestros propios emisores de eventos gracias a la pseudo-clase EventEmitter.

 

Sin embargo para entrar con los emisores de eventos primero debemos tener claros algunos conceptos, como lo son algunos patrones de estas funciones, los tipos de eventos e incluso los listener.

 

Requisitos
Para llevar a cabo los ejercicios propuestos en este tutorial debemos tener una instalación funcional de Node.js en nuestro sistema, podemos darle un vistazo a este tutorial antes de seguir profundizando en este. Es importante también poder tener acceso a un editor de texto enriquecido para hacer la codificación de los ejemplos, podemos utilizar cualquiera con el que nos sintamos cómodos, sin embargo por su facilidad de uso recomendamos Sublime Text o NotePad ++ los cuales además tienen complementos para la sintaxis JavaScript y HTML.

 

 

Patrón para los callback


La programación asíncrona no usa el retorno de valores en las funciones para denotar que esa función acaba de finalizar, sino que la misma invoca el famoso callback luego de que la operación es completada para que nuestro programa pueda continuar, donde JavaScript llevo a este tipo de programación, veamos un ejemplo en Node que lee un archivo y carga su contenido en memoria:
var fs = require('fs');
fs.readFile('archivo.txt', function(err, fileContent) {
if (err) {
throw err;
}
console.log('Contenido archivo: ', fileContent.toString());
});
Lo que hacemos acá es que estamos enviando una función anónima como el segundo argumento de la función fs.readFile, y cómo podemos ver el primer argumento de la función callback es un objeto de error, el cual tendrá una instancia de la clase Error si un error ocurre.

 

Patrón para el emisor de eventos


El estilo anterior funciona perfectamente cuando queremos notificar que una función que estamos ejecutando termina su trabajo, pero en el caso que múltiples eventos tomen lugar durante esta ejecución o muchas veces, este estilo no funcionará como queremos. Por ejemplo si queremos ser notificados cada vez que la información esté disponible en el socket, una función de tipo callback estándar no nos ayudará mucho, pero es aquí donde el emisor de eventos puede ayudarnos.

 

El emisor de eventos no es más que un objeto que como su nombre lo indica, emite un evento, donde un listener es parte del código que ata a este emisor y escucha a ciertos tipos de eventos, como por ejemplo:

var req = http.request(options, function(response) {
response.on("data", function(data) {
console.log("Alguna data de la respuesta", data);
});
response.on("end", function() {
console.log("respuesta finalizada");
});
});
req.end();
Este es un código meramente explicativo, donde podemos ver algunos de los pasos para hacer una petición HTTP a un servidor remoto pero nos permite ver como el objeto response es un emisor de eventos, el cual puede emitir no solo data y end sino otro tipo de eventos por igual.

 

Tipos de eventos


De acuerdo al ejemplo pasado pudimos ver que los eventos emitidos siempre tienen un tipo, el cual es representado por un string, en este caso “data” y “end”, los cuales serán strings arbitrarios designados por el emisor de eventos.

 

El emisor de eventos es una interfaz genérica que sirve cualquier tipo de evento, pero existe un caso especial en la implementación de Node y es el evento error, donde cada evento en el entorno emitirá un evento de este tipo cada vez que ocurra un error y si el desarrollador elige no escuchar este tipo de evento y ocurre un error, el emisor de eventos lo notará y levantará una excepción en este caso. Veamos en el siguiente código como podemos simular ese comportamiento:

var em = new (require('events').EventEmitter)();
em.emit('evento1');
em.emit('error', new Error('Mi error'));
Si lo ejecutamos por consola podremos ver como Node nos indica que no estamos manejando el error, generando así una excepción no capturada:

 

nodejs.jpg

 

Ya que hemos visto cómo se comportan los eventos de manera general, veamos como utilizamos API del emisor de eventos.

 

Usando el API del emisor de eventos


Cualquier objeto que implemente el patrón del emisor de eventos, implementa una serie de eventos como podemos ver a continuación:

 

.addListener - .on
Este método nos permite agregar un listener a un tipo de evento.

.once
Con este método podemos atar un listener a un tipo de evento dado que será llamado al menos una vez.

.removeEventListener
Este método nos permitirá retirar un listener de cualquier evento dado.

.removeAllEventListeners
Por último este método nos ayuda a retirar todos los listeners de un tipo de evento dado.

Ya viendo visto cual es la función de cada uno de estos, veamos como los utilizamos dentro de nuestros programas.

 

Usando .addListener() o .on() en callbacks


Al especificar un tipo de evento y una función callback, podemos registrar la acción a realizar cuando un evento en específico ocurra. Por ejemplo si queremos ser informados de que una porción de data está disponible y emitir un evento de tipo data podemos hacer lo siguiente:
function recibirData(data) {
console.log("Se obtuvo la data: %j", data);
}
leerFlujo.addListener("data", recibirData);
Además podemos utilizar el método .on() el cual es simplemente un atajo, veamos el equivalente a el código anterior:
function recibirData(data) {
console.log("Se obtuvo la data: %j", data);
}
leerFlujo.on("data", recibirData);
Incluso podemos añadir múltiples listeners para nuestros eventos para escuchar un mismo tipo de evento en el mismo emisor, por ejemplo:

 

manejo-eventos-nodejs-2.jpg

 

En este ejemplo lo que se hace es atar dos funciones al evento de tipo data, y donde vez que se emita el evento data, se imprimirá ambas cadenas. Es importante destacar que el emisor de eventos es el responsable de llamar a todos los listeners registrados para un tipo de evento, y este los llamará en el orden en el que fueron registrados, lo cual significa lo siguiente:

  • Un listener no podrá ser llamado inmediatamente luego que el evento es emitido, es posible que otros listeners sean llamados antes.
  • No capturar las excepciones es un comportamiento no sano para nuestro código, por lo que si alguno de estos listeners lanza un error y no es capturado, es posible que algunos listeners no sean llamados, donde podemos ilustrar esto en el siguiente ejemplo:

 

manejo-eventos-nodejs-3.jpg

 

Donde en este ejemplo el segundo listener no será invocado ya que el primero arrojo un error.

 

Usando .removeListener()


Si en algún momento ya no deseamos ser informados de los cambios en un evento u objeto en específico, podemos dejar de registrarlo especificando el tipo de evento y la función callback de la siguiente forma:

 

manejo-eventos-nodejs-4.jpg

 

 

Usando .once()


En el caso que nuestra aplicación este escuchando por un evento que pasara al menos una vez o si solo estamos interesados de que ocurra una sola vez podemos usar .once(), el cual añade el listener y lo retira una vez que el primer evento ocurre:

 

manejo-eventos-nodejs-5.jpg

 

 

Usando .removeAllListeners()


Por último podemos retirar todos los listeners para un tipo de evento en particular de un emisor de eventos de la siguiente manera:
emisor.removeAllListeners(tipo);

Creando el emisor de eventos


El emisor de eventos nos proporciona una manera genérica de crear interfaces, ya que estamos atando eventos en vez de funciones, haciendo nuestro programa más flexible, incluso si queremos usar el patrón de Node.js a lo largo de nuestra aplicación podemos crear una pseudo-clase y heredar de EventEmitter de la siguiente forma:
util = require('util');
var EventEmitter = require('events').EventEmitter;
var MiClase = function() {
}
util.inherits(MiClase, EventEmitter);
De esta forma los métodos de EventEmitter estarán disponibles para nuestra instancia y los podremos usar sin problemas y de esta forma MiClase puede emitir eventos:
MiClase.prototype.algunMetodo = function() {
this.emit("evento custom", "argumento 1", "argumento 2");
};
Aquí cuando algunMetodo es llamado en la instancia de MiClase, el ejemplo emite un evento llamado evento custom, donde a su vez emite dos datas diferentes, argumento 1 y argumento 2, la cual será enviada a los listeners de los eventos. Por último en las instancias de MiClase del lado del cliente se puede escuchar al evento custom de la siguiente forma:
var MiClase = new MiClase();
MiClase.on('evento custom', function(str1, str2) {
console.log('Evento custom escuchado con los argumentos str1 %s y str2 %s!', str1, str2);
});
Como vemos la utilización de los eventos junto con el emisor de eventos nos ayuda a la comunicación de nuestra aplicación y así finalizamos este tutorial, donde pudimos ir un más allá de la programación asíncrona y aplicando prácticas que nos ayudan a mantener un patrón estándar y óptimo para nuestras aplicaciones.


¿Te ayudó este Tutorial?


Sin comentarios, sé el primero!

No esperes más y entra en Solvetic
Deja tus comentarios y aprovecha las ventajas de la cuenta de usuario ¡Únete!

X