Node.js EventEmitter

Todas las operaciones asíncronas de E/S en Node.js envían un evento a la cola de eventos cuando se completan.

Muchos objetos en Node.js distribuyen eventos: un objeto net.Server emite un evento cada vez que hay una nueva conexión, un objeto fs.readStream emite un evento cuando se abre un archivo. Todos estos objetos que generan eventos son instancias de events.EventEmitter.

EventEmitter es el módulo central en Node.js utilizado para crear, registrar y disparar eventos.

EventEmitter es la base de la programación orientada a eventos y ayuda a los desarrolladores a implementar fácilmente el mecanismo de publicación y suscripción de eventos.


Clase EventEmitter

El módulo events solo proporciona un objeto: events.EventEmitter.

El núcleo de EventEmitter es la encapsulación de la activación de eventos y las funciones de los listeners de eventos.

Puedes acceder a este módulo medianterequire("events");para acceder a este módulo.

Ejemplo

// Introducir el módulo events
var events = require('events');
// Crear el objeto eventEmitter
var eventEmitter = new events.EventEmitter();

Si se produce un error al instanciar el objeto EventEmitter, se disparará el evento error. Cuando se añade un nuevo listener, se activa el evento newListener; cuando se elimina un listener, se activa el evento removeListener.

A continuación usamos un ejemplo sencillo para explicar el uso de EventEmitter:

Ejemplo

// Archivo event.js
var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();
event.on('some_event', function() {
        console.log('Se activó el evento some_event');
});
setTimeout(function() {
        event.emit('some_event');
}, 1000);

Al ejecutar este código, después de 1 segundo la consola mostrarásome_event disparador de eventoEl principio es que el objeto event registra un listener para el evento some_event, luego, mediante setTimeout, después de 1000 milisegundos se envía el evento some_event al objeto event, momento en el cual se llama al listener de some_event.

El resultado de la ejecución es el siguiente:

$ node event.js 
some_event 事件触发

Cada evento de EventEmitter se compone de un nombre de evento y varios parámetros. El nombre del evento es una cadena que suele expresar un determinado significado semántico. Para cada evento, EventEmitter admite varios listeners de eventos.

Cuando se dispara un evento, los listeners registrados para ese evento se invocan de forma secuencial, y los parámetros del evento se pasan como argumentos de las funciones de callback.

Expliquemos este proceso con el siguiente ejemplo:

Ejemplo

// Archivo event.js
var events = require('events');
var emitter = new events.EventEmitter();
emitter.on('someEvent', function(arg1, arg2) {
        console.log('listener1', arg1, arg2);
});
emitter.on('someEvent', function(arg1, arg2) {
        console.log('listener2', arg1, arg2);
});
emitter.emit('someEvent', 'Parámetro arg1', 'Parámetro arg2');

Al ejecutar el código anterior, el resultado de la ejecución es el siguiente:

$ node event.js 
listener1 arg1 参数 arg2 参数
listener2 arg1 参数 arg2 参数

En el ejemplo anterior, emitter registró dos listeners para el evento someEvent y luego disparó el evento someEvent.

En el resultado de la ejecución se puede observar que las funciones de callback de los dos listeners se llaman una tras otra. Este es el uso más sencillo de EventEmitter.

EventEmitter proporciona múltiples propiedades, comoonyemit。onla función se usa para vincular funciones de evento,emitla propiedad se usa para disparar un evento. A continuación, veamos en detalle la introducción a las propiedades de EventEmitter.

Métodos

N.ºMétodo y descripción
1addListener(event, listener)
Añade un listener al final de la matriz de listeners para el evento especificado.
2on(event, listener)
Registra un listener para el evento especificado; acepta una cadena event y una función de callback.
server.on('connection', function (stream) {
  console.log('someone connected!');
});
3once(event, listener)
Registra un listener de una sola vez para el evento especificado, es decir, el listener solo puede dispararse una vez y se elimina inmediatamente después de dispararse.
server.once('connection', function (stream) {
  console.log('Ah, we have our first user!');
});
4removeListener(event, listener)

Elimina un listener específico del evento especificado; el listener debe ser uno que ya haya sido registrado para ese evento.

Acepta dos parámetros: el primero es el nombre del evento y el segundo es el nombre de la función de callback.

var callback = function(stream) {
  console.log('someone connected!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
5removeAllListeners([event])
Elimina todos los listeners de todos los eventos; si se especifica un evento, elimina todos los listeners de ese evento.
6setMaxListeners(n)
De forma predeterminada, si agregas más de 10 listeners, EventEmitters mostrará un mensaje de advertencia. La función setMaxListeners se usa para cambiar el límite predeterminado del número de listeners.
7listeners(event)
Devuelve la matriz de listeners del evento especificado.
8emit(event, [arg1], [arg2], [...])
Ejecuta cada listener en el orden de los listeners; si el evento tiene listeners registrados, devuelve true; de lo contrario, devuelve false.

Métodos de clase

N.ºMétodo y descripción
1listenerCount(emitter, event)
Devuelve el número de listeners del evento especificado.
events.EventEmitter.listenerCount(emitter, eventName) //已废弃,不推荐
events.emitter.listenerCount(eventName) //推荐

Registrar un listener de evento:

Ejemplo

const EventEmitter = require('events');
const myEmitter = new EventEmitter();

// Registrar un listener de evento
myEmitter.on('sayHello', (name) => {
  console.log(`Hello, ${name}!`);
});

// Disparar el evento
myEmitter.emit('sayHello', 'Alice');

Salida:

Hello, Alice!

Usar once() para registrar un evento de una sola vez:

myEmitter.once('init', () => {
  console.log('Initialization event occurred');
});

myEmitter.emit('init'); // 打印: Initialization event occurred
myEmitter.emit('init'); // 不会再触发

Al disparar el evento, se pueden pasar parámetros para que los use el listener:

Ejemplo


myEmitter.on('data', (value1, value2) => {
  console.log(`Received values: ${value1}, ${value2}`);
});

myEmitter.emit('data', 'Node.js', 'EventEmitter');
// Salida: Received values: Node.js, EventEmitter

Si el evento disparado no tiene un listener correspondiente, EventEmitter lanzará un error:

Ejemplo

myEmitter.on('error', (err) => {
  console.error('Error event triggered:', err);
});

myEmitter.emit('error', new Error('Something went wrong'));
// Imprime: Error event triggered: Error: Something went wrong

Eventos

N.ºEvento y descripción
1newListener
  • event- Cadena, nombre del evento

  • listener- Función de manejo del evento

Este evento se dispara cuando se añade un nuevo listener.

2removeListener
  • event- Cadena, nombre del evento

  • listener- Función de manejo del evento

Elimina un listener de la matriz de listeners especificada. Ten en cuenta que esta operación cambiará los índices de los listeners que están después del listener eliminado.

Ejemplo

El siguiente ejemplo demuestra la aplicación de la clase EventEmitter mediante el evento connection (conexión).

Crea el archivo main.js, con el siguiente código:

Ejemplo

var events = require('events');
var eventEmitter = new events.EventEmitter();

// Listener n.º 1
var listener1 = function listener1() {
   console.log('Se ejecutó el listener listener1.');
}

// Listener n.º 2
var listener2 = function listener2() {
  console.log('Se ejecutó el listener listener2.');
}

// Vincular el evento connection, con la función de manejo listener1
eventEmitter.addListener('connection', listener1);

// Vincular el evento connection, con la función de manejo listener2
eventEmitter.on('connection', listener2);

var eventListeners = eventEmitter.listenerCount('connection');
console.log(eventListeners + " listeners escuchando el evento de conexión.");

// Manejar el evento connection
eventEmitter.emit('connection');

// Eliminar la función listener1 vinculada
eventEmitter.removeListener('connection', listener1);
console.log("listener1 ya no está siendo escuchado.");

// Disparar el evento de conexión
eventEmitter.emit('connection');

eventListeners = eventEmitter.listenerCount('connection');
console.log(eventListeners + " listeners escuchando el evento de conexión.");

console.log("El programa ha terminado.");

El resultado de ejecución del código anterior es el siguiente:

$ node main.js
2 个监听器监听连接事件。
监听器 listener1 执行。
监听器 listener2 执行。
listener1 不再受监听。
监听器 listener2 执行。
1 个监听器监听连接事件。
程序执行完毕。

Evento error

EventEmitter define un evento especial llamado error, que contiene la semántica del error. Cuando encontramos una excepción, generalmente disparamos el evento error.

Cuando se dispara error, EventEmitter estipula que, si no hay un listener correspondiente, Node.js lo tratará como una excepción, saldrá del programa y mostrará un mensaje de error.

Generalmente debemos configurar un listener para los objetos que puedan disparar el evento error, para evitar que todo el programa se bloquee tras encontrar un error. Por ejemplo:

var events = require('events'); 
var emitter = new events.EventEmitter(); 
emitter.emit('error'); 

Al ejecutarlo se mostrará el siguiente error:

node.js:201 
throw e; // process.nextTick error, or 'error' event on first tick 
^ 
Error: Uncaught, unspecified 'error' event. 
at EventEmitter.emit (events.js:50:15) 
at Object.<anonymous> (/home/byvoid/error.js:5:9) 
at Module._compile (module.js:441:26) 
at Object..js (module.js:459:10) 
at Module.load (module.js:348:31) 
at Function._load (module.js:308:12) 
at Array.0 (module.js:479:10) 
at EventEmitter._tickCallback (node.js:192:40) 

Heredar de EventEmitter

La mayoría de las veces no usamos EventEmitter directamente, sino que lo heredamos en los objetos. Todos los módulos centrales que admiten la respuesta a eventos, incluidos fs, net y http, son subclases de EventEmitter.

¿Por qué hacer esto? Hay dos razones:

  • Primero, la implementación de eventos en objetos con funcionalidad de entidad específica es semánticamente apropiada; la escucha y la ocurrencia de eventos deberían ser métodos de un objeto.

  • En segundo lugar, el mecanismo de objetos de JavaScript se basa en prototipos y admite cierta herencia múltiple parcial; heredar de EventEmitter no altera la relación de herencia original del objeto.

La mayoría de los módulos principales de Node.js (como HTTP y el sistema de archivos) heredan de EventEmitter; puedes crear tus propias clases para heredar de EventEmitter.

Ejemplo

class MyEmitter extends EventEmitter {}
const customEmitter = new MyEmitter();

customEmitter.on('customEvent', () => {
  console.log('Custom event fired');
});

customEmitter.emit('customEvent');
Otras extensiones