通俗讲解 发布-订阅模式

文章目录

  • 简介
    • 特点
    • 流程
  • 通俗举例
    • 角色对应
    • 运行流程
      • 1. 订阅(Subscribe)
      • 2. 发布(Publish)
      • 3. 通知(Notify)
    • 关键点
    • 代码实现
  • 注意事项

简介

发布-订阅模式是一种消息传递模式,它允许发送者(发布者)发送消息而不关心谁将接收这些消息,同样地,接收者(订阅者)可以监听他们感兴趣的消息,而不需要知道这些消息来自哪里。这种模式在JavaScript中常用于实现事件处理系统,如DOM事件监听或自定义事件系统。

特点

  • 解耦:发布者和订阅者之间不直接依赖,它们之间通过事件中心(Event Center)进行通信,实现了解耦。

  • 一对多:一个事件可以有多个订阅者,当事件被发布时,所有订阅了该事件的订阅者都会收到通知。

  • 异步通信:发布者和订阅者之间的通信是异步的,发布者发布事件时不会等待订阅者的处理结果。

  • 扩展性好:新的订阅者可以随时订阅事件,旧的订阅者也可以随时取消订阅,无需修改发布者代码。

流程

  • 定义事件总线:首先,需要定义一个事件中心对象,该对象用于管理事件和订阅者。

  • 订阅事件:订阅者通过调用事件中心的订阅方法,指定要订阅的事件类型和回调函数。

  • 发布事件:发布者通过调用事件中心的发布方法,并传递事件类型和相关数据来触发事件。

  • 触发回调:事件中心接收到发布请求后,会遍历该事件类型下的所有订阅者,并依次调用它们的回调函数,传递事件数据。


通俗举例

以下将使用消费者邮局订阅报纸邮递员 来通俗举例解释发布-订阅模式的关系:

角色对应

  • 发布者:报纸出版社(负责发布报纸内容)

  • 事件:报纸内容(如新闻、广告等)

  • 事件总线:邮局(负责传递报纸)

  • 订阅者:消费者(订阅报纸的人)

  • 邮递员:邮局的工作人员,负责将报纸从邮局传递到消费者手中

运行流程

1. 订阅(Subscribe)

  • 消费者(订阅者)到邮局(事件总线)填写订阅单,指定他们感兴趣的报纸(事件类型)。

  • 邮局(事件总线)记录消费者的订阅信息,包括消费者的地址和所订阅的报纸类型。

2. 发布(Publish)

  • 报纸出版社(发布者)完成当天的报纸编辑,将报纸内容(事件)发送到邮局(事件总线)。

  • 邮局(事件总线)接收到报纸后,会检查哪些消费者订阅了这份报纸。

3. 通知(Notify)

  • 邮局(事件总线)将报纸(事件)交给邮递员(传递者)。

  • 邮递员根据消费者的订阅信息,将报纸投递到消费者的地址(即通知订阅者)。

关键点

  • 松耦合:消费者(订阅者)和报纸出版社(发布者)之间不直接通信,而是通过邮局(事件总线)进行间接通信。

  • 事件类型:消费者(订阅者)可以订阅不同类型的报纸(如日报、晚报、财经报等),这对应于不同的事件类型。

  • 异步性:消费者(订阅者)可以在报纸发布之前或之后订阅,邮局(事件总线)会负责将后续的报纸投递给已经订阅的消费者。

代码实现

// 创建一个事件中心(模拟邮局)  
class EventCenter {
    constructor() {
        this.subscribers = {}; // 订阅者列表,按事件类型存储  
    }

    // 订阅事件(消费者订阅报纸)  
    subscribe (eventType, callback) {
        if (!this.subscribers[eventType]) {
            this.subscribers[eventType] = [];
        }
        this.subscribers[eventType].push(callback);
    }

	// 取消订阅事件(消费者取消订阅报纸)  
	unsubscribe(eventType, callback) {
	    if (this.subscribers[eventType]) {
	        const index = this.subscribers[eventType].indexOf(callback);
	        if (index !== -1) {
	            this.subscribers[eventType].splice(index, 1);
	        }
	    }
	}  

    // 发布事件(报纸出版社发布报纸)  
    publish (eventType, data) {
        // 如果有订阅者订阅了这个事件  
        if (this.subscribers[eventType]) {
            // 遍历所有订阅者,并通知他们(传递报纸)  
            this.subscribers[eventType].forEach(callback => {
                callback(data);
            });
        }
    }
    
    // 清除所有订阅者  
    clearAllSubscriptions () {
        this.subscribers = {};
    }
}
// 创建一个事件中心实例  
const eventCenter = new EventCenter();

// 消费者(订阅者)  
function consumerA (newspaper) {
    console.log('消费者A收到了报纸:', newspaper);
}

function consumerB (newspaper) {
    console.log('消费者B收到了报纸:', newspaper);
}

// 消费者A订阅日报  
eventCenter.subscribe('日报', consumerA);
// 消费者B也订阅日报  
eventCenter.subscribe('日报', consumerB);

// 报纸出版社(发布者)发布日报  
eventCenter.publish('日报', '今天的头条新闻...');

// 如果消费者C稍后订阅,他将不会收到之前发布的日报  
function consumerC (newspaper) {
    console.log('消费者C收到了报纸:', newspaper);
}

// 消费者C订阅日报(但日报已经发布过了,所以消费者C不会收到这次的报纸)  
eventCenter.subscribe('日报', consumerC);

// 如果报纸出版社再次发布日报,消费者C将会收到  
eventCenter.publish('日报', '明天的头条预告...');


// 假设消费者A后来决定不再订阅日报  
eventCenter.unsubscribe('日报', consumerA);  
  
// 报纸出版社再次发布日报,消费者A将不会收到,但消费者B和消费者C将会收到  
eventCenter.publish('日报', '后天的头条预告...');

// 清除所有订阅者  
eventCenter.clearAllSubscriptions();

// 尝试发布事件,但此时没有订阅者  
eventCenter.publish('日报', '没有订阅者将收到此消息');

注意事项

  1. 内存管理:确保在不再需要某个订阅者时取消订阅,以避免内存泄漏。

  2. 事件命名:为事件类型选择具有描述性和唯一性的名称,以避免命名冲突。

  3. 错误处理:在回调函数和事件中心的方法中添加适当的错误处理逻辑,以确保程序的健壮性。

  4. 避免无限循环:确保在回调函数中不会触发相同的事件,这可能导致无限循环。

  5. 性能考虑:当订阅者数量较多或事件发布频率较高时,需要考虑性能优化,如使用WeakMap来存储订阅者等。

  6. 兼容性:在浏览器中实现时,要注意不同浏览器对事件处理的支持情况,确保代码具有良好的兼容性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/740127.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

快速排序的实现(3种)

目录 0.快速排序1.Hoare版本1.1基本思想1.2算法描述1.3画图解释1.4问题?1.5代码实现 2.挖坑法2.1算法描述2.2画图解释2.3代码实现 3.先后指针法3.1算法描述3.2画图解释3.3代码实现 4.优化4.1优化方法4.2优化代码 5.非递归实现快排5.1算法描述 0.快速排序 1.时间复杂…

AGV选型要点及步骤,保证企业选择的AGV小车更实用

AGV AGV小车作为智能化物流仓储不可或缺的工具,在制造业得到了广泛的应用,市场需求呈现出井喷式增长。但是AGV市场还存在着很多问题,制造企业在产品选型时往往缺乏正确的引导。 AGV智能仓储 毫无疑问,我们的自动化物流系统已离不开AGV小车了,…

Spring Boot + Vue 全栈开发,都需要哪些前端知识?

Node.js默认安装的npm包和工具的位置:Node.js目录\node_modules 在这个目录下你可以看见 npm目录,npm本身就是被NPM包管理器管理的一个工具,说明 Node.js已经集成了npm工具 #在命令提示符输入 npm -v 可查看当前npm版本 npm -v 二、使用n…

【Android】Android Studio 使用Kotlin写代码时代码提示残缺问题解决

问题描述 Android Studio升级之后,从Android Studio 4.2升级到Android Studio Arctic Fox版本,因为项目比较老,使用的Gradle 版本是3.1.3,这个版本的Android Studio最低支持Gradle 3.1版本,应该算是比较合适的版本。 …

天猫超市卡有什么用?

有时候不知道在网上买什么的时候,恰好就会收到一些猫超卡、京东卡类的礼物 这真是让人苦恼 还好收卡云一直在收这些礼品卡券啊,不然我的大部分礼品卡最后只有过期的份了!

SpringBoot异常处理机制之自定义404、500错误提示页面 - 518篇

历史文章(文章累计500) 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 《国内最全的Spring Boot系列之六》 《…

c++ 正则匹配得使用

标头&#xff1a;#include <regex> 相关函数&#xff1a; regex_match regex_replace regex_search 名称描述regex_match测试正则表达式是否与整个目标字符串相完全匹配。regex_replace替换匹配正则表达式。regex_search搜索正则表达式匹配项。 1. regex_search 成功搜…

ctr/cvr预估之WideDeep模型

ctr/cvr预估之Wide&Deep模型 在探索点击率&#xff08;CTR&#xff09;和转化率&#xff08;CVR&#xff09;预估的领域中&#xff0c;我们始终追求的是一种既能捕获数据中的线性关系&#xff0c;又能发现复杂模式的模型。因子分解机&#xff08;Factorization Machines, …

张量在人工智能中的解释?

张量在人工智能中的解释&#xff1f; 张量是一种多维数组&#xff0c;它可以看作是向量和矩阵的推广。在人工智能领域&#xff0c;张量被用作数据的基本表示形式&#xff0c;尤其在深度学习中扮演着核心角色。张量的多维性允许它们表示复杂的数据结构和关系&#xff0c;而其可…

iOS之如何创建.framework静态库

番外&#xff1a;想要查看如何创建.a静态库可前往看我iOS之如何创建.a静态库-CSDN博客这篇文章。 一、创建framework项目 创建framework工程要选择iOS --> Cocoa Touch Framework输入项目名称PrintFramework也是编译生成的framework的名称。framework的名称也可以以后在项目…

不翻墙安装yolov8环境下的RT-DETR并实现PCB表面缺陷检测

目录 一、新建conda环境二、安装yolov8环境1.克隆安装包2.安装依赖包3.测试模型 任务2&#xff1a;基于RT-DETR实现PKU-PCB表面缺陷检测数据准备 数据增强测试 总结 一、新建conda环境 创建并激活conda环境&#xff1a; 在conda创建一个名为yolov8的新环境&#xff0c;并在其中…

React尚硅谷115-126(setState、Hooks、Fragment、context、组件优化、renderprops

122&#xff0c;context 只能用value传&#xff0c;可以传对象&#xff0c;字符串 一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信 使用&#xff1a; 创建Context容器对象&#xff1a; const XxxContext React.createContext() 渲染子组时&#xff0c;外面包…

SpringBoot的Web开发支持【超详细【一篇搞定】果断收藏系列】

Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println(“MyInterceptor.afterCompletion”); } } 使用Java的形式配置拦截器的拦截路径 在WebMvcConfig…

零门槛用AI,302.AI让人工智能变得简单易用

当下人工智能火爆&#xff0c;提到AI&#xff0c;几乎每个人都能说上几句&#xff0c;但是你真的会使用AI吗&#xff1f; 当涉及到如何实际使用AI时&#xff0c;许多人可能会觉得它太过高深莫测&#xff0c;从而产生一种距离感&#xff0c;不知如何开始。我和大家也一样&#x…

Apple - Game Center Programming Guide

本文翻译整理自&#xff1a;Game Center Programming Guide&#xff08; Updated: 2016-06-13 https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/GameKit_Guide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008304 文章…

jemeter基本使用

后端关验签&#xff0c;设置请求头编码和token 配置编码和token

【分布式事务】Seata AT实战

目录 Seata 介绍 Seata 术语 Seata AT 模式 介绍 实战&#xff08;nacos注册中心&#xff0c;db存储&#xff09; 部署 Seata 实现 RM 实现 TM 可能遇到的问题 1. Seata 部署成功&#xff0c;服务启动成功&#xff0c;全局事务不生效 2. 服务启动报错 can not get …

c++分隔字符串

可以使用getline函数。 有两个版本&#xff1a; 至于为什么可以使用getline函数返回值作为while的判断条件&#xff0c;cprimer中表述如下&#xff1a;

[MySql]两阶段提交

文章目录 什么是binlog使用binlog进行恢复的流程 什么是redolog缓冲池redologredolog结构 两阶段提交 什么是binlog binlog是二进制格式的文件&#xff0c;用于记录用户对数据库的修改&#xff0c;可以作用于主从复制过程中数据同步以及基于时间点的恢复&#xff08;PITR&…

Docker(六)-本地镜像发布到私有库

1.下载镜像Docker Registry 用于搭建私人版本Docker Hub docker pull registry2.运行私有库Registry 运行私有库Registry&#xff0c;相当于本地有个私有Docker hubdocker run -d -p hostPort:containerPort -v 【宿主机目录】:【容器目录】 --privilegedtrue 【私有库镜像】…