Node.js EventEmitter模块详解:监听、分发与事件管理

作者:服务小站时间:2026-10-30 17:19:53浏览:408

本文深入解析Node.js核心API中的EventEmitter模块。内容涵盖事件的监听与分发机制、回调函数中this指向的差异、同步与异步执行顺序、一次性事件监听、事件绑定的移除与管理,以及最大监听数量的限制与内存泄漏警告。通过具体代码示例,帮助开发者掌握Node.js异步事件驱动模型的实际应用。

Node.js事件驱动基础

Node.js的核心API大多基于异步事件驱动架构构建,任何能够分发事件的对象均为EventEmitter类的实例。

由于Node.js采用单线程运行模式,系统需依赖事件轮询机制,持续查询事件队列中的消息并执行对应的回调函数,其机制类似于Windows的消息映射。关于更底层的实现细节,可参考其他资料。

接下来介绍EventEmitter的具体使用方法。

事件监听与分发

1、监听事件和分发事件

EventEmitter实例可通过on或addListener方法监听事件,并使用emit()方法分发事件,示例如下:


const events = require('events'),
   EventEmitter = events.EventEmitter,
   util = require('util');
function myEmiter(){
  EventEmitter.call(this);
};
util.inherits(myEmiter,EventEmitter);//继承EventEmitter类
const myEmitterIns = new myEmiter();
myEmitterIns.on('data',(o)=>{
  console.log('receive the data:'+o.a);
});

或者使用class类


class myEmiter extends EventEmitter{}//继承EventEmitter类
const myEmitterIns = new myEmiter();

myEmitterIns.on('data',(o)=>{
  console.log('receive the data:'+o.a);
});
myEmitterIns.emit('data',{a:1});

执行结果如下:

E:\developmentdocument\nodejsdemo>node event-example.js receive the data:1

回调参数与this指向

2、向事件监听回调函数传递参数

从上述示例可见,emit()方法可向回调函数传递任意参数集合。需注意,this关键字指向调用emit方法的EventEmitter实例,但在箭头函数中例外,其this指向全局this,因为箭头函数的this在定义时已绑定。示例如下:


class myEmiter extends EventEmitter{}
const myEmitterIns = new myEmiter();
myEmitterIns.on('data',function(data){
  console.log("普通回调函数中this:");
  console.log(this);
});
myEmitterIns.on('data1',(data1)=>{
  console.log("箭头回调函数中this:");
  console.log(this);
});
myEmitterIns.emit('data',{a:1});
myEmitterIns.emit('data1',{a:1});

执行结果如下:

E:\developmentdocument\nodejsdemo>node event-example.js 普通回调函数中this: myEmiter { domain: null, _events: { data: [Function], data1: [Function] }, _eventsCount: 2, _maxListeners: undefined } 箭头回调函数中this: {}

关于箭头函数中的this,需明确其内部未绑定this机制,而是使用外层作用域的this,因此箭头函数不能作为构造函数使用。

事件执行顺序与异步处理

3、事件监听程序的执行顺序

EventEmitter实例可绑定多个事件。当顺序触发这些事件时,EventEmitter以同步模式执行,即前一个事件处理函数未完成时,不会触发下一个事件,示例如下:


class myEmiter extends EventEmitter{}
const myEmitterIns = new myEmiter();
myEmitterIns.on('data',function(data){
  console.time('data事件执行了');
  for(var i = 0 ; i< 100000; i++)
    for(var j = 0 ; j< 100000; j++)
      ;
  console.timeEnd('data事件执行了');
});
myEmitterIns.on('data1',(data1)=>{
  console.log("data1事件开始执行...");
});
myEmitterIns.emit('data',{a:1});
myEmitterIns.emit('data1',{a:1});

执行结果如下:

E:\developmentdocument\nodejsdemo>node event-example.js data事件执行了: 4721.401ms data1事件开始执行...

若需在回调函数中使用异步操作,如setTimeout、setImmediate或process.nextTick()等,则可实现异步效果,示例如下:


myEmitterIns.on('data',function(data){
  setImmediate(()=>{
    console.log('data事件执行了...');
  });
});

执行结果如下:

E:\developmentdocument\nodejsdemo>node event-example.js data1事件执行了... data事件执行了...

一次性事件与绑定管理

4、一次性事件监听

EventEmitter可使用once监听特定事件,该处理程序仅触发一次,后续emit调用将被忽略,因为监听程序已被注销,示例如下:


myEmitterIns.once('one',(data)=>{
  console.log(data);
});
myEmitterIns.emit('one','this is first call!');
myEmitterIns.emit('one','this is second call!');

执行结果如下:

E:\developmentdocument\nodejsdemo>node event-example.js this is first call!

从结果可见,'one'事件仅执行了一次。

5、移除事件绑定

类似于DOM事件监听,EventEmitter也可移除事件绑定,利用removeListener(eventName,listener)方法解除特定事件的绑定。因此,回调函数listener必须为命名函数,否则无法找到该函数,因为函数是引用类型,即使函数体相同,也不是同一函数,示例如下:


myEmitterIns.on('data',function(e){
  console.log(e);
});
myEmitterIns.removeListener('data',function(e){
  console.log(e);
});
myEmitterIns.emit('data','hello data!');
function deal(e){
  console.log(e);
}
myEmitterIns.on('data1',deal);
myEmitterIns.removeListener('data1',deal);
myEmitterIns.emit('data1','hello data1!');

执行结果如下:

E:\developmentdocument\nodejsdemo>node event-example.js hello data! E:\developmentdocument\nodejsdemo>

从执行结果看,data事件使用匿名函数,因此未被移除,而data1事件成功解除绑定。需注意,emit触发某事件后,所有绑定的回调函数均会被调用,即使在某回调中使用removeListener移除另一回调,对当前触发队列无效,但后续事件队列将移除该回调。示例如下:


function dealData1(e){
  console.log('data事件执行了...A');
}
myEmitterIns.on('data',function(e){
  console.log(e);
  myEmitterIns.removeListener('data',dealData1);//这里解除dealData1的绑定
});
myEmitterIns.on('data',dealData1);
myEmitterIns.emit('data','data事件执行了...B');
/*执行结果为:
 data事件执行了...B
 data事件执行了...A
*/
//再次触发该事件时,dealData1回调已经被解除绑定了
myEmitterIns.emit('data','data事件执行了...');
//data事件执行了...

此外,可使用removeAllListeners()解除所有事件的绑定。

监听数量管理与限制

6、获取事件监听数量和监听函数

使用emitter.listenerCount(eventName)函数获取指定事件的监听数量,emitter.listeners(eventName)函数则用于获取指定事件的所有监听函数,示例如下:


var cbA = ()=>{},
  cbB = ()=>{};
var emitter = new myEmiter();
emitter.on('data',cbA);
emitter.on('data',cbB);
console.log('emitter实例的data事件绑定了%d个回调函数',emitter.listenerCount('data'));
console.log('它们是:',emitter.listeners('data'));

执行结果如下:

E:\developmentdocument\nodejsdemo>node event-example.js emitter实例的data事件绑定了2个回调函数 它们是: [ [Function: cbA], [Function: cbB]]

7、获取和设置emitter的最大监听数量 Node.js建议同一事件的监听数量不超过10个,可查看EventEmitter.defaultMaxListeners属性得知,示例如下:


console.log(EventEmitter.defaultMaxListeners);//结果为10个

emitter通过getMaxListeners()方法获取最大监听数量以及setMaxListeners(n)方法设置最大监听数量,示例如下:


var cbA = ()=>{},
  cbB = ()=>{};
var emitter = new myEmiter();
emitter.setMaxListeners(1);
emitter.on('data',cbA);
emitter.on('data',cbB);
console.log(emitter.getMaxListeners());

执行结果如下:

E:\developmentdocument\nodejsdemo>node event-example.js emitter的事件最大监听数是:1 (node:6808) Warning: Possible EventEmitter memory leak detected. 2 data listener s added. Use emitter.setMaxListeners() to increase limit

如上结果所示,若设置了最大监听数量,同一事件的监听最好不要超过该最大值,否则可能引发内存泄漏。

events模块介绍至此。希望对大家的学习有所帮助,也希望大家多多支持本站。

作者最新文章