1. EventEmitter是什么?
- Node.js 的每个API都是异步的,并作为一个独立线程运行,使用异步函数调用。基本所有的事件机制都是用设计模式中的观察者模式实现。
- Node.js中有多个内置事件,可以通过引入events模块,并通过实例化EventEmitter类来绑定和监听事件。
- Node.js里面的许多对象都会分发事件:一个net.Server对象会在每次有新连接时分发一个事件, 一个fs.readStream对象会在文件被打开的时候发出一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。
2. 使用EventEmitter类注册事件监听器
events
模块只提供了一个对象:events.EventEmitter
。其核心就是事件触发与事件监听功能的封装。- 创建
EventEmitter
对象:
var events = require('events'); // 引入 events 模块
var eventEmitter = new events.EventEmitter(); // 创建 eventEmitter 对象
- 事件绑定及事件触发:EventEmitter 提供了多个属性,如 on 和 emit。on 函数用于绑定事件函数,emit 属性用于触发一个事件。
eventEmitter.on('eventName', eventHandler); // 绑定事件处理程序
eventEmitter.emit('eventName'); // 触发事件
- 创建main.js文件,代码如下:
var events = require('events'); // 引入 events 模块
var eventEmitter = new events.EventEmitter(); // 创建 eventEmitter 对象
var connectHandler = function connected() { // 创建事件处理程序
console.log('连接成功。');
eventEmitter.emit('data_received'); // 触发 data_received 事件
}
eventEmitter.on('connection', connectHandler); // 绑定 connection 事件处理程序
eventEmitter.on('data_received', function(){ // 使用匿名函数绑定 data_received 事件
console.log('数据接收成功。');
});
eventEmitter.emit('connection'); // 触发 connection 事件
console.log("程序执行完毕。");
- 上面代码的运行结果为:
$ node main.js
连接成功。
数据接收成功。
程序执行完毕。
EventEmitter
的每个事件由一个事件名和若干参数组成,事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter
支持若干个事件监听器。当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。
//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', 'arg1 参数', 'arg2 参数');
- 执行以上代码,运行的结果如下:
$ node event.js
listener1 arg1 参数 arg2 参数
listener2 arg1 参数 arg2 参数
- 上面的例子中,emitter 为事件 someEvent 注册了两个事件监听器,当someEvent 事件触发后,两个事件监听器回调函数被先后调用。
3. EventEmitter 常用属性
3.1 方法
on(event, listener)
:为指定事件注册一个监听器,接受一个字符串event 和一个回调函数。
server.on('connection', function (stream) {
console.log('someone connected!');
});
emit(event, [arg1], [arg2], [...])
:按参数的顺序执行每个监听器,如果事件有注册监听则返回true, 否则返回false。addEventListener(event, listener)
:为指定事件添加一个监听器到监听器数组的尾部。once(event, listener)
: 为指定事件注册一个单次监听器,即监听器最多只能触发一次,触发后立即解除该监听器。
server.once('connection', function (stream) {
console.log('Ah, we have our first user!');
});
removeListener(event, listener)
:移除指定事件的某个监听器,监听器必须是该事件已经注册过的。
var callback = function(stream) {
console.log('someone connected!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback); // 移除之前注册过的某个事件监听器
removeAllListeners([event])
:参数可选。没有参数则移除所有事件的所有监听器;如果指定事件参数,则移除指定事件的所有监听器。setMaxListeners(n)
:EventEmitters 默认添加超过10个监听器就会输出警告信息。可以使用该方法提高监听器的默认限制数量。listeners(event)
:返回事件的监听器数组。
3.2 类方法
listenerCount(emitter, event)
:返回指定事件的监听器数量。通过EventEmitter类进行调用,而不是通过类对象调用:
var listenerNum = require('events').EventEmitter.listenerCount(eventEmitter,'connection');
3.3 事件
newListener(event, listener)
:该事件在添加新监听器时被触发。removeListener(event, listener)
:从指定监听器数组中删除一个监听器。该操作会改变处于被删监听器之后的那些监听器的索引。实例演示:
// main.js文件
var events = require('events');
var eventEmitter = new events.EventEmitter();
var listener1 = function listener1() { // 定义监听器 1
console.log('监听器 listener1 执行。');
}
var listener2 = function listener2() { // 定义监听器 2
console.log('监听器 listener2 执行。');
}
eventEmitter.addListener('connection', listener1); // 用 addListener 方法绑定 connection 事件,处理函数为 listener1
eventEmitter.on('connection', listener2); // 用 on 方法绑定 connection 事件,处理函数为 listener2
var eventListeners = require('events').EventEmitter.listenerCount(eventEmitter,'connection'); // 用 listenerCount 类方法得到connection事件的监听器数量
console.log(eventListeners + " 个监听器监听连接事件。");
eventEmitter.emit('connection'); // 触发 connection 事件
eventEmitter.removeListener('connection', listener1); // 移除监绑定的 listener1 函数
console.log("listener1 不再受监听。");
eventEmitter.emit('connection'); // 触发 connection 事件
eventListeners = require('events').EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners + " 个监听器监听连接事件。");
console.log("程序执行完毕。");
- 上面代码的执行结果为:
$ node main.js
2 个监听器监听连接事件。
监听器 listener1 执行。
监听器 listener2 执行。
listener1 不再受监听。
监听器 listener2 执行。
1 个监听器监听连接事件。
程序执行完毕。
4. error 事件
EventEmitter
定义了一个特殊的事件error
,它包含了错误的语义,在遇到异常时会触发error
事件。- 当
error
被触发时,如果没有绑定error
的监听器,就会出现异常,导致程序退出并输出错误信息。因此需要为会触发error
事件的对象设置监听器,避免遇到错误后整个程序崩溃。
var events = require('events');
var emitter = new events.EventEmitter();
emitter.emit('error');
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)
5. 继承 EventEmitter
- 通常并不会直接使用 EventEmitter ,而是在对象中继承它。包括 fs、net、http,只要是支持事件响应的核心模块都是 EventEmitter 的子类。
- 使用继承的原因有两点:
- 具有某个实体功能的对象实现事件符合语义,事件的监听和发生应该是一个对象的方法;
- JS 的对象机制是基于原型的,支持多继承,继承 EventEmitter 不会影响对象原有的继承关系。