手写 EventHub

813 阅读2分钟

什么是 EventHub

在了解 EventHub 之前,不知道大家是否熟悉一种设计模式叫做发布订阅模式,而 EventHub 也就是以这为基础实现的。那么什么是“发布订阅模式”呢?

发布订阅模式

用订报纸来打比方,A 家庭订购了“xx日报”,这就是“订阅”。而在订阅以后,每次“xx日报”发布新一天的报纸,这一过程就是发布,然后新报纸会送到 A 的家门口。 也就是说,“xx日报”每次发布新的内容,都会被已经订阅的 A 接收到。当然,可以有多个订阅者。

EventHub

现在来聊一聊 EventHub 到底是什么。 可以把 EventHub 当作一个事件处理中心,或者一个赏金猎人协会。在 EventHub 中,委托人作为“发布者”,可以在里面发布、委托信息,然后赏金猎人作为“订阅者”,可以领取信息,一个信息可以被多个赏金猎人合作完成。

开始手写 EventHub

确定 API

一般来说,一个 EventHub 对象需要有下面三种方法:onoffemit,其中,用 on 方法来监听事件,emit 来触发事件,off 来解除监听,那么我们的 EventHub 对象应该这么来设计:

class EventHub {
    on(eventName: string, fn: (data: unknown) => void) {
        //...
    }
    
    off(eventName: string, fn: (data: unknown) => void) {
        //...
    }
    
    emit(eventName: string, data?: unknown) {
        //...
    }
}

为 EventHub 添加存储对象

我们需要一个对象,把每次用 on 监听的函数存储起来,确保在 emitoff 的时候能够找到对应的事件和函数

class EventHub {
    private cache:  { [key: string]: Array<(data: unknown) => void> } = {}
    //...
}

在每次使用 on 添加监听事件时,就会把监听事件的名称作为 key 放入 cache 对象中,将事件将要触发的回调函数放入到这个 key 对应的数组中。就如这样的形式:

cache: {
    'xx日报': [fn1, fn2],
    'xx晚报': [fn3, fn4],
    ...
}

最后结果代码

class EventHub {
  private cache: { [key: string]: Array<(data: unknown) => void> } = {};

  on(eventName: string, fn: (data: unknown) => void) {
    // 把 fn 放入 this.cache[eventName]
    this.cache[eventName] = this.cache[eventName] || [];
    this.cache[eventName].push(fn);
  }

  emit(eventName: string, data?: unknown) {
    // 把 this.cache[eventName] 里的 fn 全部执行一遍
    if (this.cache[eventName] === undefined) {
      this.cache[eventName] = [];
    }
    (this.cache[eventName] || []).forEach(fn => fn(data));
  }

  off(eventName: string, fn: (data: unknown) => void) {
    // 把 fn 从 this.cache[eventName] 数组中删除
    const index = indexOf(this.cache[eventName], fn);
    if (index === -1) return;
    this.cache[eventName].splice(index, 1);
  }
}

export default EventHub;

/**
 * 帮助函数 indexOf
 * @param array
 * @param item
 */
function indexOf(array: Array<(data: unknown) => void>, item: (data: unknown) => void) {
  if (array === undefined) return -1;

  let index = -1;
  for (let i = 0; i < array.length; i++) {
    if (array[i] === item) {
      index = i;
      break;
    }
  }
  return index;
}