Node中不少模块都可以使用EventEmitter,有了EventEmitter才能方便的进行事件的监听。下面看一下Node.js中的EventEmitter如何使用。node
EventEmitter是对事件触发和事件监听功能的封装,在node.js中的event模块中,event模块只有一个对象就是EventEmitter,下面是一个最基本的使用方法:数组
var EventEmitter = require('events').EventEmitter; var event = new EventEmitter(); event.on('some_event', function() { console.log('some_event 事件触发'); }); setTimeout(function() { event.emit('some_event'); }, 1000);
上面的代码中首先实例化了一个EventEimitter对象,而后就能够进行事件的监听以及发布。经过on方法对特定的事件进行监听,经过emit方法对事件进行发布。在1s后发布一个"some_event"事件,这个时候就会自动被event对象经过on进行监听,并触发对应的回调方法。app
EventEmitter实例对象支持的方法列表以下:
emitter.on(name, f) //对事件name指定监听函数f emitter.once(name, f) //与on方法相似,可是监听函数f是一次性的,使用后自动移除 emitter.listeners(name) //返回一个数组,成员是事件name全部监听函数 emitter.removeListener(name, f) //移除事件name的监听函数f emitter.removeAllListeners(name) //移除事件name的全部监听函数 ......
同时,事件的发布emit方法能够传入多个参数,第一个参数是定义的事件,后面其余参数回做为参数传递到监听器的回调函数中。dom
为了可以更容易理解其内部监听事件的实现的原理,因此本身封装了一个模块以下:
function EventEmitter(){ this._events = {};//初始化一个私有的属性 } //type 绑定的事件名 // listen 事件发生后的监听 function EventEmitter(){ this._events = {};//初始化一个私有的属性 } //type 绑定的事件名 // listen 事件发生后的监听 EventEmitter.prototype.on = EventEmitter.prototype.addListener= function(type,listener){ if(this._events[type]){ this._events[type].push(listener); }else{ this._events[type] = [listener]; } }; EventEmitter.prototype.once = function(type,listener){ function callOnce() { listener.apply(this, arguments); this.removeListener(type, callOnce); } this.on(type, callOnce); }; EventEmitter.prototype.emit = function(type){ var callbacks = this._events[type]; var args = Array.prototype.slice.call(arguments,1); var self = this; callbacks.forEach(function (callback) { callback.apply(self, args); }) }; EventEmitter.prototype.removeListener=function(type,listener){ if(this._events[type]){ var listeners = this._events[type]; for(var i=0;i<listeners.length;i++){ if(listeners[i] === listener){ listeners.splice(i,1); return; } } } }; module.exports = EventEmitter;
经过如下方式实现:异步
var EventEmitter = require('./EventEmitter'); var util = require('util'); function Girl(name){ this.name = name; EventEmitter.call(this); } util.inherits(Girl,EventEmitter); var girl = new Girl(); function Boy(name){ this.name = name; this.say = function(thing){ console.log(thing); } } var xiaoming = new Boy('小明'); var xiaohua = new Boy('小花'); //注册监听 事件 订阅 girl.addListener('看',xiaoming.say); //注册监听 事件 订阅 girl.on('看',xiaohua.say); //发射事件 发布 girl.emit('看','钻石'); girl.removeListener('看',xiaoming.say); //girl.removeAllListeners('看'); girl.emit('看','项链');
以下一些关键点是须要注意的
eventEmitter.emit() 方法容许将任意参数传给监听器函数。 当一个普通的监听器函数被 EventEmitter 调用时,标准的 this 关键词会被设置指向监听器所附加的 EventEmitter。函数
const myEmitter = new MyEmitter(); myEmitter.on('event', function(a, b) { console.log(a, b, this); // 打印: // a b MyEmitter { // domain: null, // _events: { event: [Function] }, // _eventsCount: 1, // _maxListeners: undefined } }); myEmitter.emit('event', 'a', 'b');
也可使用 ES6 的箭头函数做为监听器。可是这样 this 关键词就再也不指向 EventEmitter 实例:oop
const myEmitter = new MyEmitter(); myEmitter.on('event', (a, b) => { console.log(a, b, this); // 打印: a b {} }); myEmitter.emit('event', 'a', 'b');
EventEmitter 会按照监听器注册的顺序同步地调用全部监听器。 因此须要确保事件的正确排序且避免竞争条件或逻辑错误。 监听器函数可使用 setImmediate() 或 process.nextTick() 方法切换到异步操做模式:ui
const myEmitter = new MyEmitter(); myEmitter.on('event', (a, b) => { setImmediate(() => { console.log('这个是异步发生的'); }); }); myEmitter.emit('event', 'a', 'b');
当 EventEmitter 实例中发生错误时,会触发一个 'error' 事件。 这在 Node.js 中是特殊状况。
若是 EventEmitter 没有为 'error' 事件注册至少一个监听器,则当 'error' 事件触发时,会抛出错误、打印堆栈跟踪、且退出 Node.js 进程。this
const myEmitter = new MyEmitter(); myEmitter.emit('error', new Error('whoops!')); // 抛出错误,并使 Node.js 崩溃
为了防止 Node.js 进程崩溃,能够在使用 domain 模块。 (注意,domain 模块已被废弃。)
做为最佳实践,应该始终为 'error' 事件注册监听器。spa
const myEmitter = new MyEmitter(); myEmitter.on('error', (err) => { console.error('有错误'); }); myEmitter.emit('error', new Error('whoops!')); // 打印: 有错误
新增于: v0.1.26 eventName <any> listener <Function> 从名为 eventName 的事件的监听器数组中移除指定的 listener。 const callback = (stream) => { console.log('有链接!'); }; server.on('connection', callback); // ... server.removeListener('connection', callback); removeListener 最多只会从监听器数组里移除一个监听器实例。 若是任何单一的监听器被屡次添加到指定
eventName 的监听器数组中,则必须屡次调用 removeListener 才能移除每一个实例。
注意,一旦一个事件被触发,全部绑定到它的监听器都会按顺序依次触发。 这意味着,在事件触发后、最后一个监听器完成执行前,任何 removeListener() 或 removeAllListeners() 调用都不会从 emit() 中移除它们。 随后的事件会像预期的那样发生。
const myEmitter = new MyEmitter(); const callbackA = () => { console.log('A'); myEmitter.removeListener('event', callbackB); }; const callbackB = () => { console.log('B'); }; myEmitter.on('event', callbackA); myEmitter.on('event', callbackB); // callbackA 移除了监听器 callbackB,但它依然会被调用。 // 触发是内部的监听器数组为 [callbackA, callbackB] myEmitter.emit('event'); // 打印: // A // B // callbackB 被移除了。 // 内部监听器数组为 [callbackA] myEmitter.emit('event'); // 打印: // A
由于监听器是使用内部数组进行管理的,因此调用它会改变在监听器被移除后注册的任何监听器的位置索引。 虽然这不会影响监听器的调用顺序,但意味着由 emitter.listeners() 方法返回的监听器数组副本须要被从新建立。
返回一个 EventEmitter 引用,能够链式调用