猴都会写的EventHub(发布-订阅)👀

388 阅读2分钟

手写EventBus 作为经典的前端手写面试题,在工作之余我觉得还是得记录一下😊 什么是EventHub

EventHub是基于发布订阅模式实现的一个实例, 翻译为事件中心,常用于多模块(组件)间的通信。

今天我们就简单的实现EventHub的发布、订阅和取消订阅用一个简单的例子-定闹钟: 首先我们确定Api:

  • on 订阅(打开某时的闹钟开关
  • emit 发布(时间到了,大懒猪快起床
  • off 取消订阅(关闭闹钟开关

实现发布和订阅

我们得用一个变量eventMap来暂存事件名字(比如:闹钟)和事件(比如铃声响),这里我们用一个数组来记录事件(因为考虑多次订阅):

class EventHub {
  private eventMap: { [key: string]: Array<(params?) => void> } = {};
  on(eventName, fn) {}
  emit(eventName) {}
  off(eventName) {}
}
export default EventHub;

当我们订阅一个事件的时候,我们把这个事件push到订阅的东西的事件队列中,比如我在手机闹钟里面订了个3秒后的闹钟:

import EventHub from "../src/index";

const eventHub = new EventHub();

eventHub.on("闹钟", () => {
  setTimeout(() => {
    console.log("闹钟响了");
  }, 3000);
});

eventHub.emit("闹钟");

然后我们在类里面的on函数把事件push进去:

class EventHub {
  private eventMap: { [key: string]: Array<(params?:any) => void> } = {};
  on(eventName: string, fn: (params?: any) => void) {
    this.eventMap[eventName] = this.eventMap[eventName] || [];
    this.eventMap[eventName].push(fn);
  }
  emit(eventName) {}
  off(eventName) {}
}

export default EventHub;

我们在emit触发执行逻辑:

class EventHub {
  private eventMap: { [key: string]: Array<(params?: any) => void> } = {};
  on(eventName: string, fn: (params?: any) => void) {
    this.eventMap[eventName] = this.eventMap[eventName] || [];
    this.eventMap[eventName].push(fn);
  }
  emit(eventName: string) {
    (this.eventMap[eventName] || []).forEach((fn) => fn());
  }
  off(eventName: string) {
    console.log(eventName);
  }
}

export default EventHub;

执行触发的代码,看控制台(提示:node 运行ts文件请下载ts-node)

image.png 实现取消订阅: 当我们在emit 之前 off闹钟的订阅

import EventHub from "../src/index";

const eventHub = new EventHub();

const fn = () => {
  setTimeout(() => {
    console.log("闹钟响了");
  }, 3000);
};
eventHub.on("闹钟", fn);
eventHub.off("闹钟", fn);
eventHub.emit("闹钟");

加上取消订阅的逻辑:

 off(eventName: string, fn: (params?: any) => void) {
    const index = this.eventMap[eventName].findIndex((item) => item === fn);
    if (index === -1) return;
    this.eventMap[eventName].splice(index, 1);
    console.log("取消闹钟成功");
  }

执行取消成功:

image.png 源代码点击