发布——订阅模式
在实现 TicTacToe 的时候用到了这个,因此记录一下。
1. 概述
发布-订阅模式是一种消息传递范式,其中“发布者”(publisher)发送消息,而无需知道哪些“订阅者”(subscriber)会接收它。这是一种强大的解耦机制。
2. 模式核心
这个模式主要由下面这些内容组成:
- 发布者 (Publisher):事件的发布方,它只管发布特定主题(topic)的事件。
- 订阅者 (Subscriber):事件的接收方,它订阅自己感兴趣的主题。
- 事件中心 (Event Hub/Broker):一个中介,负责管理订阅和发布。它维护着一个主题和订阅者列表的映射。
3. 简单实现
我们可以用一个 class
来实现这个模式。在 JavaScript 中实现这个模式需要注意一些事项:
- 注意我们使用的是回调函数。
- 注意微任务队列的使用:它既保证了不阻塞同步任务、又保证了在根据同步结果进行渲染前先执行异步任务。但是**并不是任何情况都要使用微任务队列的!**之后在分析 TicTacToe 项目实现时会仔细分析:
class PubSub {
constructor() {
// 存储所有主题和对应的订阅者回调
// 格式: { 'topic1': [cb1, cb2], 'topic2': [cb3] }
this.events = {};
}
/**
* 订阅一个主题
* @param {string} topic - 主题名称
* @param {function} callback - 回调函数
* @returns {object} - 包含 unsubscribe 方法的对象,方便取消订阅
*/
subscribe(topic, callback) {
if (!this.events[topic]) {
this.events[topic] = [];
}
this.events[topic].push(callback);
// 返回一个对象,包含取消订阅的方法
return {
unsubscribe: () => this.unsubscribe(topic, callback)
};
}
/**
* 取消订阅
* @param {string} topic - 主题名称
* @param {function} callback - 要移除的回调函数
*/
unsubscribe(topic, callback) {
if (this.events[topic]) {
this.events[topic] = this.events[topic].filter(cb => cb !== callback);
}
}
/**
* 发布一个主题
* @param {string} topic - 主题名称
* @param {*} data - 传递给回调函数的数据
*/
publish(topic, data) {
if (this.events[topic]) {
this.events[topic].forEach(callback => {
// 使用 queueMicrotask 可以在当前同步代码执行完后,再通知订阅者
// 这样可以避免在发布过程中,某个订阅者的耗时操作阻塞后续通知
queueMicrotask(() => {
callback(data);
});
});
}
}
}