发布订阅eventBus与eventEmitter

399 阅读2分钟

前言

一提到“发布订阅者模式”我就会想到eventBus,但 eventEmitter这个词我还是第一次见到,它和eventBus难道不是一个东西么?

eventBus的实现原理

class EventBus {
    constructor() {
        this.events = {};
    }

    //订阅
    subscribe(names, cb) {
        this.events[names] = this.events[names] || []
        this.events[names].push(cb)
    }

    //发布
    publish(names, val) {
        if (!this.events[names]) {
            return
        }
        this.events[names].forEach(callback => callback(val))

    }

    //取消
    unsubscribe(names, cb) {
        if (!this.events[names]) {
            return
        }
        this.events[names] = this.events[names].filter(item => item !== cb)
    }
}

//使用
let eventBus = new EventBus()


function cb1(...args) {
    console.log("你好1", ...args)
}
function cb2(...args) {
    console.log("你好2", args)
}
//订阅
eventBus.subscribe('event1', cb1)
eventBus.subscribe('event1', cb2)
eventBus.subscribe('event2', cb2)
//发布
eventBus.publish('event1', [1, 2, 3]) //你好1 [ 1, 2, 3 ] 你好2 [ 1, 2, 3 ]
eventBus.publish('event1', '我是测试二') // 你好1 我是测试二 你好2 我是测试二

//取消
eventBus.unsubscribe('event1', cb1)
eventBus.publish('event1', [1, 2, 3]) //你好2 [ 1, 2, 3 ]

eventEmitter的实现原理

class EventEmitter {
  constructor() {
    this._events = {};
  }

  //   //注册事件
  //   on(eventName, cb) {
  //     const cbs = this._events[eventName] || []; //若本身注册过,则取之前的值,否则置为空数组
  //     cbs.push(cb);
  //     this._events[eventName] = cbs;
  //   }
  //   //发布订阅,并调度执行
  //   emit(eventName, ...args) {
  //     const cbs = this._events[eventName] || [];
  //     //将调度中心同名注册下的所有事件都执行一遍
  //     cbs.forEach((cb) => cb(...args));
  //   }

  //   off(eventName, cb) {
  //     const cbs = this._events[eventName] || [];
  //     //第二个并级判断,是为了配合once的特殊注册
  //     this._events[eventName] = cbs.filter(
  //       (filtItem) => filtItem !== cb && filtItem.cb !== cb
  //     );
  //   }
  // //思想:在执行一次过后就将对应函数off掉
  //   once(eventName, cb) {
  //     const one = (...args) => {
  //       cb(...args);
  //       this.off(eventName, one);
  //     };
  //     //此处非常重要,若不添加,则在once注册之后,emit之前就off则无法删除对应注册回调
  //     one.cb = cb;
  //     this.on(eventName, one);
  //   }

  //订阅
  on(eventName, cb) {
    this._events[eventName] = this._events[eventName] || []
    this._events[eventName].push(cb)
  }
  //发布
  emit(eventName, ...args) {
    this._events[eventName] = this._events[eventName] || []
    this._events[eventName].forEach(callback => {
      callback(...args)
    })
  }
  //取消
  off(eventName, cb) {
    this._events[eventName] = this._events[eventName] || []
    this._events[eventName] = this._events[eventName].filter(item != cb && item.cb != cb)
  }

  //一次(执行一次后自动取消)
  once(eventName, cb) {
    const one = (...args) => {
      cb(...args)
      //off
      this.off(eventName, one);
    }
    one.cb = cb //此处特别需要注意
    this.on(eventName, one)
  }
}

const events = new EventEmitter();
function handleName(name) {
  console.log("name1", name);
}
function handleName2(name) {
  console.log("name2", name);
}

events.on("hello", handleName);
events.once("hello", handleName2);
//此时once注册的时候存储的回调函数跟实际传递的回调函数是不相等的,在没有emit之前就直接off,是没有办法删除的
events.off("hello", handleName2);

events.emit("hello", "小明");
events.emit("hello", "小王");

两者的区别

从实现原理上面看,其实两个并没有什么区别

在实际使用当中:
`eventBus` 的使用范围更加广泛,可以跨越不同组件和模块之间进行信息通信传递,它是一个全局概念的事件总线。通常作为一个单例对象存在,因此往往需要创建一个中央管理器的实例
`EventEmitter` 是一个基于类的模块,用于在单个组件或模块内部实现事件的发布和订阅。所以它可以在需要的地方创建实例对象,并将其用于内部事件的发布和订阅