亚囡 发表于 2023-11-25 16:16:16

[JS] 事件总线

事件总线与发布订阅模式

事件总线是对发布-订阅模式的一种实现。
发布-订阅模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
发布-订阅模式实现了松耦合,发布者不是直接将消息发送给订阅者,而是经过了一个中间的代理,事件总线就是一种中间代理的实现。
事件总线维护了一个事件列表,订阅者可以订阅某一个事件,并指定一个回调(回调的具体实现在订阅者内部);
每个事件又维护了一个依赖列表,发布者可以“触发”一个事件,事件总线负责遍历该事件的依赖列表,调用每一个当初订阅者订阅时指定的回调函数。
在 JS 中实现Event Bus

定义一个EventBus类:
class EventBus{}需要维护一个事件列表,在初始化事件总线对象的时候创建。
对于每一个事件,我们需要记录它的事件名(string类型),还需要记录该事件的依赖列表(Array类型),依赖列表其实就是各个订阅者的回调函数的列表。
这采用了一个对象来记录多个事件,刚好键值对就是事件名:依赖列表
constructor(){
    this.eventObject = {};
}实现订阅:
每一次订阅需要指定订阅的事件名和发布时要触发的回调函数。

[*]如果指定的事件不存在,则添加一个事件,并推入该新依赖(回调函数)。
[*]如果指定的事件存在,则直接推入新依赖(回调函数)
subscribe(eventName, callback){
    if(!this.eventObject){
      this.eventObject = [];
    }
    this.eventObject.push(callback);
}实现发布:
发布也要考虑到指定的事件是否存在。如果不存在,则中断并返回警告;如果存在指定事件,则依次调用事件的依赖列表(回调列表)。
publish(eventName){
    const callbackList = this.eventObject;
    if(!callbackList)return console.warn(eventName + " Not Found!");
    for(let callback of callbackList){
      callback();
    }
}汇总如下:
class EventBus{
    constructor(){
      this.eventObject = {};
    }

    /**
   * @param {string} eventName
    */
    publish(eventName){
      const callbackList = this.eventObject;

      if(!callbackList)return console.warn(eventName + " Not Found!");
      
      for(let callback of callbackList){
            callback();
      }
    }

    /**
   * @param {string} eventName
   * @param {Function} callback
    */
    subscribe(eventName, callback){
      if(!this.eventObject){
            this.eventObject = [];
      }

      this.eventObject.push(callback);
    }
}优化Event Bus的实现

在发布时传递参数

使用...args介绍不定长参数列表,在发布时传入,并在调用回调函数列表的时候依次传入。
/**
* @param {string} eventName
*/
publish(eventName, ...args){
    const callbackList = this.eventObject;

    if(!callbackList)return console.warn(eventName + " Not Found!");

    for(let callback of callbackList){
      callback(...args);
    }
}提供取消订阅的操作

在订阅者调用subscribe方法订阅事件的时候,返回一个用于取消订阅的unSubscribe方法。
在实现事件的回调函数列表的时候,需要为每一个回调函数添加一个id,方便以后查询并删除该回调函数。
这里将订阅的回调函数列表换成用对象结构存储,因为在数组中删除某个中间元素较麻烦且耗时,效率不如对象结构的delete删除键值对。
换成对象结构存储后,键值对表示:id:回调函数。
class EventBus{
    constructor(){
      // 初始化事件列表
      this.eventObject = {};
      // 回调函数列表的 id
      this.callbackId = 0;
    }

    /**
   * @param {string} eventName
    */
    publish(eventName, ...args){
      // 取出该事件的回调函数列表(对象)
      const callbackObject = this.eventObject;

      if(!callbackObject)return console.warn(eventName + " Not Found!");
      
      // 执行每一个回调函数,这里的id是对象的key
      for(let id in callbackObject){
            callbackObject(...args);
      }
    }

    /**
   * @param {string} eventName
   * @param {Function} callback
   * @returns {{
   *      unSubscribe: Function
   * }}
    */
    subscribe(eventName, callback){
      if(!this.eventObject){
            this.eventObject = {};
      }
      // 为当前事务的回调函数申请一个专属id
      const id = this.callbackId++;
      // 绑定回调函数
      this.eventObject = callback;
      // 生成取消订阅的函数
      const unSubscribe = ()=>{
            // 删除该回调函数
            delete this.eventObject;
            // 如果该事件的回调函数都删完了,则顺便删除事件列表中的事件
            if(Object.keys(this.eventObject).length === 0){
                delete this.eventObject;
            }
      }
      
      return {unSubscribe};
    }
}清除某个事件

/**
* @description 清除某事件
* @param {string} eventName
*/
clear(eventName){
    if(!this.eventObject.hasOwnProperty(eventName)){
      return console.warn(eventName + " Not Found!");
    }
    delete this.eventObject;
}
来源:https://www.cnblogs.com/feixianxing/p/js-event-bus-publish-subscribe-pattern.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: [JS] 事件总线