什么是 EventHub
在了解 EventHub 之前,不知道大家是否熟悉一种设计模式叫做发布订阅模式,而 EventHub 也就是以这为基础实现的。那么什么是“发布订阅模式”呢?
发布订阅模式
用订报纸来打比方,A 家庭订购了“xx日报”,这就是“订阅”。而在订阅以后,每次“xx日报”发布新一天的报纸,这一过程就是发布,然后新报纸会送到 A 的家门口。 也就是说,“xx日报”每次发布新的内容,都会被已经订阅的 A 接收到。当然,可以有多个订阅者。
EventHub
现在来聊一聊 EventHub 到底是什么。 可以把 EventHub 当作一个事件处理中心,或者一个赏金猎人协会。在 EventHub 中,委托人作为“发布者”,可以在里面发布、委托信息,然后赏金猎人作为“订阅者”,可以领取信息,一个信息可以被多个赏金猎人合作完成。
开始手写 EventHub
确定 API
一般来说,一个 EventHub 对象需要有下面三种方法:on、off、emit,其中,用 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 监听的函数存储起来,确保在 emit 和 off 的时候能够找到对应的事件和函数
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;
}