订阅与发布者模式

理解 发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖与它的对象都将得到状态改变的通知。 发布-订阅模式由三房组成:订阅者、发布者、调度中心。订阅者把自己想订阅的事件注册到调度中心,当发布者发布该事件到调度中心,也就是该事件触发时,由调度中心统一调度订阅者注册到调度中心的处理代码。 现实中的例子:用户(订阅者)到邮局(调度中心)订阅期刊,杂志社(发布者)定期送期刊到报刊亭。报刊亭根据自己的订阅列表将不同的期刊送给订阅过该期刊的用户。 实现思路 参考 vue 的 on/emit 方法 创建一个对象(类) 在该对象上创建一个缓存列表 新建 on 方法。该方法用来将订阅者提供的函数加到缓存列表中 新建 emit 方法。发布者发布消息调用emit方法,从缓存列表中取得对应的函数并执行 代码实现 简化实现:没有区别具体的事件类型 调度中心对象(类): ```javascript // event.js /** * @file 事件的订阅与发布 * @author 甘露平 */ export class Event { constructor(eventName) { this._name = eventName; this._callBacks = []; } // 增加事件 addHandler(cb) { this._callBacks.push(cb); } // 移除事件 removeHandler(cb) { this._callBacks.forEach((value, index) => { if(value === cb) { this._callBacks.splice(index, 1); } }); } // 针对订阅的事件进行发布 raise(...param) { for (let i = 0, count = this._callBacks.length; i < count; i++) { this._callBacks[i](...param); } } // 针对异步事件进行发布并返回值 async raiseAsync(...param) { let res = []; let reqArr = this._callBacks.map( async callBack => { return await callBack(...param); } ); for (let i = 0; i < reqArr.length; i++) { res[i] = reqArr[i]; } return res; } } ``` 订阅者: ```javascript // index.js import { CommandBar } from './components/CommandBar'; import { DataList } from './components/DataList'; const commandBar = new CommandBar(); const loading = new Loading(); $(() => { commandBar.subscribeQueryEvent(commandBarQueryHandler); commandBar.init(); }) // 命令栏查询事件 async function commandBarQueryHandler(param) { // 显示待加载 loading.show(); try { const _config = { url: `${businessPrefix}/report/list`, method: 'get', params: param } const _tableData = await sendRequest(_config); // 将数据传递到表格组件进行处理 dataList.setDataSource(_tableData.items); // 关闭待加载 loading.hide(); } catch (error) { // error loading.hide(); // 提示信息 layer.msg('获取数据报错'); } } ``` 发布者: ```javascript // CommandBar.js import { Event } from '../../../common/event'; export class CommandBar { constructor() { this._evQuery = new Event('CommandBar-Query'); // dom绑定 this._domBind(); } // 命令栏组件初始化 async init() { //... } // dom绑定 _domBind() { // ... } // 点击查询事件 _handleQueryClick() { // 命令栏进行数据的处理 const _obj = { reportDate: '', unitCode: '' } // 发布消息 this._evQuery.raise(_obj); } // 订阅查询事件 subscribeQueryEvent(handler) { this._evQuery.addHandler(handler); } } ``` - 基本的发布-订阅模式类 ```javascript class Event { constructor () {} // 首先定义一个事件容器,用来装事件数组(因为订阅者可以是多个) handlers = {} // 事件添加方法,参数有事件名和事件方法 addEventListener (type, handler) { // 首先判断handlers内有没有type事件容器,没有则创建一个新数组容器 if (!(type in this.handlers)) { this.handlers[type] = [] } // 将事件存入 this.handlers[type].push(handler) } // 触发事件两个参数(事件名,参数) dispatchEvent (type, ...params) { // 若没有注册该事件则抛出错误 if (!(type in this.handlers)) { return new Error('未注册该事件') } // 便利触发 this.handlers[type].forEach(handler => { handler(...params) }) } // 事件移除参数(事件名,删除的事件,若无第二个参数则删除该事件的订阅和发布) removeEventListener (type, handler) { // 无效事件抛出 if (!(type in this.handlers)) { return new Error('无效事件') } if (!handler) { // 直接移除事件 delete this.handlers[type] } else { const idx = this.handlers[type].findIndex(ele => ele === handler) // 抛出异常事件 if (idx === -1) { return new Error('无该绑定事件') } // 移除事件 this.handlers[type].splice(idx, 1) if (this.handlers[type].length === 0) { delete this.handlers[type] } } } } ``` 参考链接 JavaScript 发布-订阅模式 JS的发布订阅模式

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):