【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')
// ...

 

总结: 使用事件的好处就是:自己不用时刻关注对方,而是让对方主动通知自己


 

  





 

posted @ 2018-09-18 13:44  220和284  阅读(162)  评论(0)    收藏  举报