【nodejs】event 事件模块
Node 的事件模块events主要包含一个类: EventEmitter。
这个类在node的内置模块和第三方模块中大量使用。所有触发事件的对象都是EventEmitter类的实例。这些对象开放了一个eventEmitter.on()函数,允许将一个或多个函数绑定到会被对象触发的命名事件上。当对象触发一个事件时,所有绑定在该事件上的函数都会被同步的调用。
例子 eventEmitter.on() 方法用于注册监听器,eventEmitter.emit()方法用于触发事件。
const EventEmitter = require('events');
const myEmitter = new EventEmitter ();
myEmitter.on('event', () => {
console.log('触发了一个事件!');
});
myEmitter.emit('event');
// >>>
// 触发了一个事件!
Node.js 中 任何实现了事件发射器模式的对象(比如HTTP请求、文件IO等),都继承EventEmitter类 下面我们看一下 EventEmitter类 的定义代码
class EventEmitter { // 返回正在监听名为 eventName的事件的监听器数量 static listenerCount(emitter: EventEmitter, type: string | number): number; // 每个事件 默认可注册监听器的最大数量 static defaultMaxListeners: number; // 修改事件 监听器数量的限制 setMaxListeners(n: number): this; // 调用指定名称事件的 所有监听器 emit(type: string | number, ...args: any[]): boolean; // 添加listener 函数到名为 type 的事件监听器数组末尾 addListener(type: string | number, listener: Listener): this; // addListener 方法的别名 on(type: string | number, listener: Listener): this; // 添加一个单次listener 函数到名为 eventName的事件。下次触发eventName事件时,监听器会被移除,然后调用 once(type: string | number, listener: Listener): this; // 从type 的事件监听器数组中移除指定的 listener removeListener(type: string | number, listener: Listener): this; // 移除全部或指定type的监听器 removeAllListeners(type?: string | number): this; // 返回type事件下的监听器数组 listeners(type: string | number): Listener[]; // 返回type事件下监听器数量 listenerCount(type: string | number): number; }
下面我们详细分析一下 EventEmitter类的定义 并尝试模拟实现 events 模块所提供的功能
首先我们先来看一下有关 添加事件监听的方法,.on 与.addListener 是指向的同一个方法,所以它们没有什么不同之处, 都是添加一个事件监听器
// 这里我们模拟 EventEmitter 类的定义实现了一个简单的具有添加事件监听器和触发事件的类 class MyEmitter { // 首先初始化对象的时候创建了一个事件的容器 constructor () { this.eventsPool = {} } // 添加事件监听器方法 将监听器添加到指定类型下的数组中 addListener (type, listener) { if (!this.eventsPool) { this.eventsPool = {} } if(!this.eventsPool[type]){ this.eventsPool[type]=[] } this.eventsPool[type].push(listener) } // 触发事件方法 遍历类型下所有事件监听器并调用 emit (type, ...args) { let listeners = this.eventsPool[type] if (listeners) { for (let listener of listeners) { listener(args) } } } }
测试我们实现的 addListener方法 和 emit方法 功能,一切正常!看起来并没有什么不同
const myEmitter = new MyEmitter(); myEmitter.addListener('event', () => { console.log('触发了一个事件!'); }); myEmitter.emit('event'); // >>> // 触发了一个事件!
然后我们再来看一下 另一个添加监听器的方法 .once 添加一个单次listener 函数, 其实.once 与 .addListener 方法区别只是在对listener 进行了简单修改
看代码:
class MyEmitter { // 首先初始化对象的时候创建了一个事件的容器 constructor () { this.eventsPool = {} } // 添加事件监听器方法 将监听器添加到指定类型下的数组中 once (type, listener) { if (!this.eventsPool) { this.eventsPool = {} } if(!this.eventsPool[type]){ this.eventsPool[type]=[] } // >>> once 对listener 进行改造 const onceListener = (...args) => { let listeners = this.eventsPool[type] let lIndex = listeners.findIndex(listener => listener === onceListener) if (lIndex !== -1) { listeners.splice(lIndex, 1) } listener.apply(this, args) } // >>> // >>> 将改造好的listener 放入监听器数组 this.eventsPool[type].push(onceListener) } // 触发事件方法 遍历类型下所有事件监听器并调用 emit (type, ...args) { let listeners = this.eventsPool[type] if (listeners) { for (let listener of listeners) { listener(args) } } } }
测试一下我们 实现once 方法的功能
const myEmitter = new MyEmitter() myEmitter.once('once', function() { console.log('once') }) myEmitter.emit('once') myEmitter.emit('once') // >>> 只输出一次once // once
上面代码中尽管我们触发了两次事件 但是仅打印输出了一次 说明我们写的once添加的事件监听器确实只会在第一次触发
在上面已经把EvemtEmitter类添加事件的方法进行模拟实现,下面看一下有关移除事件监听器的方法
首先 .removeListener(type, listener) 移除type事件下的 listener监听器
class MyEmitter { constructor () { this.eventsPool = {} } // 添加事件监听器方法 将监听器添加到指定类型下的数组中 addListener (type, listener) { if (!this.eventsPool) { this.eventsPool = {} } if(!this.eventsPool[type]){ this.eventsPool[type]=[] } this.eventsPool[type].push(listener) } // 触发事件方法 遍历类型下所有事件监听器并调用 emit (type, ...args) { let listeners = this.eventsPool[type] if (listeners) { for (let listener of listeners) { listener(args) } } } // >>> 从事件监听器数组中删除当前方法 removeListener (type, listener) { let listeners = this.eventsPool[type] let lIndex = listeners.findIndex(ilistener => ilistener === listener) if (lIndex !== -1) { listeners.splice(lIndex, 1) } } // >>> }
还是测试运行一下 removeListener 方法的功能
``` const myEmitter = new MyEmitter() const oper_a = function() { console.log('aaa') } myEmitter.addListener('aaa', oper_a) myEmitter.addListener('aaa', oper_a) myEmitter.emit('aaa') // >>> // aaa // aaa // 移除aaa事件上的一个监听器 oper_a myEmitter.removeListener('aaa', oper_a) myEmitter.emit('aaa') // >>> // aaa ```
最后看一下移除全部或指定type的监听器的方法 removeAllListeners(type?: string | number)
class MyEmitter { constructor() { this.eventPool = {} } // 添加事件监听器方法 将监听器添加到指定类型下的数组中 addListener (type, listener) { if (!this.eventsPool) { this.eventsPool = {} } if(!this.eventsPool[type]){ this.eventsPool[type]=[] } this.eventsPool[type].push(listener) } // 触发事件方法 遍历类型下所有事件监听器并调用 emit (type, ...args) { let listeners = this.eventsPool[type] if (listeners) { for (let listener of listeners) { listener(args) } } } // >>> 与removeListener稍微不同 removeAllListeners (type) { if (type) { delete this.eventsPool[type] } else { this.eventsPool = {} } } // >>> }
测试一下我们的 removeAllListeners 方法,最后输出只有 bbb 说明removeAllListeners('aaa') 确实起到了作用
const myEmitter = new MyEmitter() const a = function () { console.log('aaa') } const b = function () { console.log('bbb') } myEmitter.addListener('aaa', a) myEmitter.addListener('aaa', a) myEmitter.addListener('bbb', b) // 移除aaa类型下所有事件监听器 myEmitter.removeAllListeners('aaa') myEmitter.emit('aaa') myEmitter.emit('bbb') // >>> 最后输出, // bbb
下面讲一个故事:
// 在没有烽火台时,敌寇侵犯 // 周幽王: 来人啊,传 aa 去通知 xx 诸侯,出兵保护本王 const zhuhou_A = new ZhuHou() const zhuhou_B = new ZhuHou() // ... // 信使需要找到具体的诸侯 const xinshi = new XinShi() xinshi.inform(zhuhou_A) xinshi.inform(zhuhou_B) // 周幽王需要找到具体信使 const zhou = new King()
// 在有了烽火台之后, 敌寇侵犯 // 周幽王: 别废话,快点烽火台 const zhuhou_A = new ZhuHou() const zhuhou_B = new ZhuHou() zhuhou_A.addListener('fire', function() {...}) zhuhou_B.addListener('fire', function() {...}) // 周幽王不需要知道具体的人,只要发送一个信号就好了 const zhou = new King() zhou.emit('fire')
zhou.emit('fire')
zhou.emit('fire')
zhou.emit('fire')
// ...
zhuhou_A.removeListener('fire')
zhuhou_B.removeListener('fire')
// ...
zhou.emit('fire')
zhou.emit('fire')
zhou.emit('fire')
// ...
总结: 使用事件的好处就是:自己不用时刻关注对方,而是让对方主动通知自己

浙公网安备 33010602011771号