发布订阅
发布订阅模式解决什么问题?
- 问题: 消息的发布方与接收方不应互相知道对方的存在;
- 解法: 在两者之间加入订阅中心, 发布方按主题发布, 订阅中心按主题把消息转发给订阅者;
- 收益: 发布方与订阅者彻底解耦, 可一对多广播, 也可动态增删订阅;
发布订阅模式包含哪些角色?
- Publisher: 发布者, 负责把消息交给订阅中心;
- ConcretePublisher: Publisher 的具体实现;
- MessageCenter: 订阅中心, 定义
subscribe、unsubscribe、publish; - Subscriber: 订阅者, 定义订阅、取消订阅与更新方法;
发布订阅与观察者模式有什么区别?
- 观察者模式: 观察者知道自己的目标, 由目标直接通知观察者;
- 发布订阅模式: 发布者不认识订阅者, 消息经订阅中心转发;
- 关系: 发布订阅可以看作在观察者之上又增加了一层中介, 见 观察者;
如何用 TypeScript 实现事件订阅中心?
interface SubscribeEvent {
fn: (...args: any[]) => void;
once: boolean;
}
class EventEmitter {
private subscribes = new Map<string, Set<SubscribeEvent>>();
private addEvent(type: string, fn: (...args: any[]) => void, once: boolean) {
let sub = this.subscribes.get(type);
if (!sub) {
sub = new Set();
this.subscribes.set(type, sub);
}
sub.add({ fn, once });
}
on(type: string, callback: (...args: any[]) => void) {
this.addEvent(type, callback, false);
}
once(type: string, callback: (...args: any[]) => void) {
this.addEvent(type, callback, true);
}
emit(type: string, ...args: any[]) {
const sub = this.subscribes.get(type);
if (!sub) return;
for (const event of [...sub]) {
event.fn(...args);
if (event.once) sub.delete(event);
}
}
off(type: string, callback: (...args: any[]) => void) {
const sub = this.subscribes.get(type);
if (!sub) return;
for (const event of sub) {
if (event.fn === callback) sub.delete(event);
}
}
}
如何使用事件订阅中心?
const emitter = new EventEmitter();
emitter.once("once", () => console.log("once test"));
emitter.emit("once"); // once test
emitter.emit("once"); // 不再触发
const fn = () => console.log("off");
emitter.on("on", fn);
emitter.on("on", () => console.log("no off"));
emitter.off("on", fn);
emitter.emit("on"); // no off
- 遍历安全:
emit先复制订阅集合再执行回调, 避免回调中增删订阅影响本次遍历;