阿西河

所有教程

公众号
🌙
阿西河前端的公众号

我的收藏

    最近访问  (文章)

      教程列表

      抓包专区
      测试专区

      Node.js events EventEmitter 类

      Node.js events EventEmitter 类

      新增于: v0.1.26 EventEmitter 类由 events 模块定义:

      const EventEmitter = require('events');
      

      当新增监听器时,会触发 ‘newListener’ 事件;

      当移除已存在的监听器时,则触发 ‘removeListener’ 事件。

      EventEmitter是node事件模型的根基,由EventEmitter为基础构建的事件驱动架构处处体现着异步编程的思想,因此,我们在构建node程序时也要遵循这种思想。

      EventEmitter 类的属性/方法/事件

      事件驱动原理:观察者模式

      在事件驱动系统里,事件是如何产生的?一个事件发生为什么能”自动”调用回调函数?我们先看看观察者模式。

      观察者(Observer)模式是一种设计模式,应用场景是当一个对象的变化需要通知其他多个对象而且这些对象之间需要松散耦合时。在这种模式中,被观察者(主体)维护着一组其他对象派来(注册)的观察者,有新的对象对主体感兴趣就注册观察者,不感兴趣就取消订阅,主体有更新的话就依次通知观察者们。说猿话就是:

      function Subject() {
          this.listeners = {}
      }
      
      Subject.prototype = {
          // 增加事件监听器
          addListener: function(eventName, callback) {
              if(typeof callback !== 'function')
                  throw new TypeError('"listener" argument must be a function')
      
              if(typeof this.listeners[eventName] === 'undefined') {
                  this.listeners[eventName] = []
              } 
              this.listeners[eventName].push(callback) // 放到观察者对象中
          },
          // 取消监听某个回调
          removeListener: function(eventName, callback) {
              if(typeof callback !== 'function')
                  throw new TypeError('"listener" argument must be a function')
              if(Array.isArray(this.listeners[eventName]) && this.listeners[eventName].length !== 0) {
                  var callbackList = this.listeners[eventName]
                  for (var i = 0, len=callbackList.length; i < len; i++) {
                      if(callbackList[i] === callback) {
                          this.listeners[eventName].splice(i,1)     // 找到监听器并从观察者对象中删除
                      }
                  }
                  
              }
          },
          // 触发事件:在观察者对象里找到这个事件对应的回调函数队列,依次执行
          triggerEvent: function(eventName,...args) {
              if(this.listeners[eventName]) {
                  for(var i=0, len=this.listeners[eventName].length; i<len; i++){
                      this.listeners[eventName][i](...args)
                  }
              }
          }
      }
      

      OK,我们现在来添加监听器和发送事件:

      var event = new Subject()
      function hello() {
          console.log('hello, there')
      }
      event.addListener('hello', hello)
      event.triggerEvent('hello')     //    输出 hello, there
      event.removeListener('hello', hello) // 取消监听
      setTimeout(() => event.triggerEvent('hello'),1000) // 过了一秒什么也没输出
      

      在观察者模式中,注册的回调函数即事件监听器,触发事件调用各个回调函数即是发布消息。

      你可以看到,观察者模式只不过维护一个信号对应函数的列表,可以存,可以除,你只要给它信号(索引),它就按照这个信号执行对应的函数,也就相当于间接调用了。那直接调用函数不就行了,干嘛写的那么拐弯抹角?刚才也说了,这是因为观察者模式能够解耦对象之间的关系,实现了表示层和数据逻辑层的分离,并定义了稳定的更新消息传递机制。

      回到开始的问题,事件是如何产生又“自动”被调用的?是像上面那样当调用event.triggerEvent的时侯产生的吗?并不是,调用event.triggerEvent就相当于调用了回调函数,是事件执行过程,而事件产生过程则更多由底层来产生并通知给node的。我们拿node的全局变量 process来举例,process是EventEmitter的实例:

      process.on('exit', (code) => {
        console.log(`About to exit with code: ${code}`);
      });
      

      node执行时会在process的exit事件上绑定你指定的回调,相当于调用了上面的 addListener ,而当你退出进程时,你会发现你指定的函数被执行了,但是你没有手动调用触发exit事件的方法,也就是上面的 triggerEvent ,这是因为node底层帮你调用了——操作系统底层使这个进程退出了,node会得到这个信息,然后触发事先定义好的触发方法,回调函数就因此依次执行了。像这样的内置事件是node模块事先写好并开放出来的,使用时直接绑定回调函数即可,如果要自定义事件,那就得自己发送信号了。

      上面代码实现了最基本的观察者模式,node 源码中EventEmitter的实现原理跟这差不多,除了这些还加入了其他有用的特性,而且各种实现都尽可能使用性能最好的方式(node源码真是处处反映着智慧的光芒)。

      
      node中众多模块都继承了 `EventEmitter` ,比如文件模块系统下的 `FSWatcher` :
      
      const EventEmitter = require('events')
      const util = require('util')
      ...
      
      function FSWatcher() {
        EventEmitter.call(this);// 调用构造函数
        ...
      }
      util.inherits(FSWatcher, EventEmitter); // 继承 EventEmitter
      

      其他模块也是如此。它们一同组成了node的异步事件驱动架构。

      EventEmitter的JS实现

      实现一个EventEmitter类,作为被观察的对象:

      class EventEmitter {  
        constructor() {
          this.listeners = new Map();
        }
        addListener(label, callback) { }
        removeListener(label, callback) { }
        emit(label, ...args) {  }
      }
      
      • this.listeners 使用Map类型保存不同事件对应的监听者的处理函数。
      • addListenerremoveListener 分别是添加和移除监听函数。
      • emit 出发某类事件,并用args传递数据。

      下面是具体实现:

      class EventEmitter {
          constructor() {
              this.listeners = new Map();
          }
      
          addListener(label, callback) {
              this.listeners.has(label) || this.listeners.set(label, []);
              this.listeners.get(label).push(callback);
          }
          removeListener(label, callback) {
              let listeners = this.listeners.get(label);
              let index;
              if (listeners && listeners.length) {
                  index = listeners.reduce((i, listener, index) => {
                      return (isFunction(listener) && listener === callback) ? i = index : i;
                  }, -1);
              }
              if (index > -1) {
      
                  listeners.splice(index, 1);
                  this.listeners.set(label, listeners);
                  return true;
              }
      
              return false;
          }
          emit(label, ...args) {
              let listeners = this.listeners.get(label);
              if (listeners && listeners.length) {
                  listeners.forEach((listener) => {
                      listener(...args);
                  })
                  return true;
              }
      
              return false;
          }
      }
      

      再实现一个观察者:

      class Observer {
          constructor(id, subject) {
              this.id = id;
              this.subject = subject;
          }
          on(label, callback) {
              this.subject.addListener(label, callback);
          }
      }
      

      观察者类使用subject保存被观察的对象,也就上述的EventEmitter对象。 通过 on 给被观察者对象绑定事件以及事件处理函数。

      好了,基本工作基本完成:

      let observable = new EventEmitter();
      
      let [observer1, observer2] = [
          new Observer(1, observable),
          new Observer(2, observable)
      ]
      
      observer1.on('change', (data) => {
          console.log(`${observer1.id} observered data:`, data);
      })
      
      observer2.on('haha', (data) => {
          console.log(`${observer2.id} observered data:`, data);
      })
      
      observable.emit('change', {a: 1}); // 1 observered data: { a: 1 }
      observable.emit('haha', [1, 2, 3]); // 2 observered data: [ 1, 2, 3 ]
      

      更多内容请参考:Node.js events 事件触发器,或者通过 点击对应菜单 进行查看;


      目录
      目录