当前位置: 首页 > news >正文

WHAT - 发布订阅

目录

  • 一、常见实现方案
    • 1.1 使用事件发射器(Event Emitter)
    • 1.2 自定义事件系统(EventBus)
    • 1.3 使用库如 PubSubJS
    • 1.4 使用框架内置的状态管理工具
      • Vue.js
      • React (使用 Context API 或 Redux)
  • 二、先后关系
    • 2.1 缓存事件数据
    • 2.2 使用 Redux 或 Vuex 等状态管理工具
    • 2.3 使用本地存储或 IndexedDB

在前端开发中,发布订阅是一种常见的开发场景,允许一个对象(发布者)发布事件,而多个对象(订阅者)可以订阅并接收这些事件。

发布订阅在设计模式中,可以理解为 观察者模式 / Observer Pattern

一、常见实现方案

以下是一些常见的实现方案:

1.1 使用事件发射器(Event Emitter)

许多 JavaScript 框架和库内置了事件发射器机制,例如 Node.js 的 EventEmitter 类。

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();// 定义事件
eventEmitter.on('event', (data) => {console.log('Event received:', data);
});// 触发事件
eventEmitter.emit('event', 'Hello World!');

1.2 自定义事件系统(EventBus)

自己实现一个简单的发布订阅系统,可以通过维护一个事件监听器的映射表来实现。

class EventBus {constructor() {this.listeners = {};}on(event, listener) {if (!this.listeners[event]) {this.listeners[event] = [];}this.listeners[event].push(listener);}emit(event, data) {if (this.listeners[event]) {this.listeners[event].forEach(listener => listener(data));}}off(event, listener) {if (this.listeners[event]) {this.listeners[event] = this.listeners[event].filter(l => l !== listener);}}
}const eventBus = new EventBus();
eventBus.on('message', (data) => console.log('Message received:', data));
eventBus.emit('message', 'Hello EventBus!');

1.3 使用库如 PubSubJS

PubSubJS 是一个轻量级的 JavaScript 发布订阅库。

const PubSub = require('pubsub-js');// 订阅
const token = PubSub.subscribe('TOPIC', (msg, data) => {console.log(msg, data);
});// 发布
PubSub.publish('TOPIC', 'Hello PubSubJS!');// 取消订阅
PubSub.unsubscribe(token);

1.4 使用框架内置的状态管理工具

许多现代前端框架如 Vue.js、React 和 Angular 提供了内置的状态管理工具,可以用来实现发布订阅模式。例如:

Vue.js

const EventBus = new Vue();// 组件A:发布事件
EventBus.$emit('myEvent', 'Hello from Component A');// 组件B:订阅事件
EventBus.$on('myEvent', (data) => {console.log(data);
});

React (使用 Context API 或 Redux)

// 使用 Context API
const MyContext = React.createContext();// 提供者组件
const MyProvider = ({ children }) => {const [state, setState] = useState(null);const publish = (data) => {setState(data);};return (<MyContext.Provider value={{ state, publish }}>{children}</MyContext.Provider>);
};// 订阅者组件
const MySubscriber = () => {const { state, publish } = useContext(MyContext);useEffect(() => {console.log('State updated:', state);}, [state]);

二、先后关系

可以先订阅后发布,那可以先发布后订阅吗?

在发布订阅模式中,先订阅后发布是非常常见的做法,因为这通常是实现实时事件通知的基本方式:订阅者先准备好接收消息,然后发布者发送消息

然而,某些情况下,也可能需要在没有订阅者存在的情况下发布消息,并且在订阅者稍后订阅时能够收到之前发布的消息。要实现这种“先发布后订阅”的机制,可以采用以下几种方法:

2.1 缓存事件数据

发布者在发布消息时,将消息暂时存储在一个缓存中,当新的订阅者订阅时,可以将缓存中的消息发送给订阅者。

class EventBus {constructor() {this.listeners = {};this.cachedEvents = {};}on(event, listener) {if (!this.listeners[event]) {this.listeners[event] = [];}this.listeners[event].push(listener);// 如果有缓存的事件,立即触发if (this.cachedEvents[event]) {listener(this.cachedEvents[event]);}}emit(event, data) {if (this.listeners[event]) {this.listeners[event].forEach(listener => listener(data));}// 缓存事件数据this.cachedEvents[event] = data;}off(event, listener) {if (this.listeners[event]) {this.listeners[event] = this.listeners[event].filter(l => l !== listener);}}
}const eventBus = new EventBus();// 发布事件
eventBus.emit('message', 'This is a cached message');// 订阅事件
eventBus.on('message', (data) => {console.log('Message received:', data); // Output: 'This is a cached message'
});

2.2 使用 Redux 或 Vuex 等状态管理工具

在前端框架中使用状态管理工具,例如 Redux(React)或 Vuex(Vue.js),可以在状态发生变化时订阅并触发相应的处理逻辑。状态管理工具的状态是持久的,订阅者在任何时候都可以获取当前的状态。

Redux 示例

const { createStore } = require('redux');// 定义 action 类型
const SET_MESSAGE = 'SET_MESSAGE';// 定义 action 创建函数
const setMessage = (message) => ({type: SET_MESSAGE,payload: message
});// 定义 reducer
const messageReducer = (state = null, action) => {switch (action.type) {case SET_MESSAGE:return action.payload;default:return state;}
};// 创建 Redux store
const store = createStore(messageReducer);// 订阅 store
const unsubscribe = store.subscribe(() => {const state = store.getState();console.log('State updated:', state);
});// 发布 action
store.dispatch(setMessage('This is a Redux message'));

2.3 使用本地存储或 IndexedDB

如果需要跨页面持久化数据,可以使用浏览器的本地存储(LocalStorage)或 IndexedDB。发布者将消息存储到本地存储中,订阅者在订阅时从本地存储中读取数据。

// 发布消息
localStorage.setItem('message', 'This is a persisted message');// 订阅消息
const cachedMessage = localStorage.getItem('message');
if (cachedMessage) {console.log('Message received:', cachedMessage);
}

通过这些方法,可以实现“先发布后订阅”的功能,确保订阅者能够收到之前发布的消息。选择哪种方法可以根据具体需求和技术栈来决定。

相关文章:

WHAT - 发布订阅

目录 一、常见实现方案1.1 使用事件发射器&#xff08;Event Emitter&#xff09;1.2 自定义事件系统&#xff08;EventBus&#xff09;1.3 使用库如 PubSubJS1.4 使用框架内置的状态管理工具Vue.jsReact (使用 Context API 或 Redux) 二、先后关系2.1 缓存事件数据2.2 使用 Re…...

React@16.x(23)useEffect

目录 1&#xff0c;介绍作用介绍 2&#xff0c;注意点2.1&#xff0c;参数1&#xff0c;副作用函数2.1.1&#xff0c;运行时间点2.1.2&#xff0c;返回值2.1.3&#xff0c;闭包的影响2.1.4&#xff0c;严禁出现在代码块中&#xff08;判断&#xff0c;循环&#xff09;2.1.5&am…...

算法竞赛一句话解题经典问题分析 ©ntsc 2024

原名&#xff1a;算法竞赛一句话解题&经典问题分析 ©ntsc 2024 处理进度 绿&#xff1a;P1381【~P&#xff08;今日进度&#xff09;】蓝&#xff1a;P1099 致CSDN网友&#xff1a; 本文章不定期更新&#xff01;文章链接&#xff1a; 经典问题分析 基础知识与编程…...

【TensorFlow深度学习】强化学习中的贝尔曼方程及其应用

强化学习中的贝尔曼方程及其应用 强化学习中的贝尔曼方程及其应用&#xff1a;理解与实战演练贝尔曼方程简介应用场景代码实例&#xff1a;使用Python实现贝尔曼方程求解状态价值结语 强化学习中的贝尔曼方程及其应用&#xff1a;理解与实战演练 在强化学习这一复杂而迷人的领…...

牛客 NC129 阶乘末尾0的数量【简单 基础数学 Java/Go/PHP/C++】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/aa03dff18376454c9d2e359163bf44b8 https://www.lintcode.com/problem/2 思路 Java代码 import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff…...

【Spring Boot】异常处理

异常处理 1.认识异常处理1.1 异常处理的必要性1.2 异常的分类1.3 如何处理异常1.3.1 捕获异常1.3.2 抛出异常1.3.4 自定义异常 1.4 Spring Boot 默认的异常处理 2.使用控制器通知3.自定义错误处理控制器3.1 自定义一个错误的处理控制器3.2 自定义业务异常类3.2.1 自定义异常类3…...

Laravel学习-自定义辅助函数

因为laravel框架的辅助函数helpers不会进入版本库&#xff0c;被版本库忽略的&#xff0c;只有自己创建一个helpers辅助函数。 可以在任意文件下创建helpers.php文件&#xff0c;建议在app目录下&#xff0c; 然后在composer.json文件中&#xff0c;autoload 中间&#xff0c…...

LLVM Cpu0 新后端6

想好好熟悉一下llvm开发一个新后端都要干什么&#xff0c;于是参考了老师的系列文章&#xff1a; LLVM 后端实践笔记 代码在这里&#xff08;还没来得及准备&#xff0c;先用网盘暂存一下&#xff09;&#xff1a; 链接: https://pan.baidu.com/s/1yLAtXs9XwtyEzYSlDCSlqw?…...

GAT1399协议分析(9)--图像上传

一、官方定义 二、wirechark实例 有前面查询的基础,这个接口相对简单很多。 请求: 文本化: POST /VIID/Images HTTP/1.1 Host: 10.0.201.56:31400 User-Agent: python-requests/2.32.3 Accept-Encoding: gzip, deflate Accept: */* Connection: keep-alive content-type:…...

Spring ApplicationContext的getBean方法

Spring ApplicationContext的getBean方法 在Spring框架的ApplicationContext中&#xff0c;getBean(Class<T> requiredType)方法可以接受一个类类型参数&#xff0c;这个参数可以是接口类也可以是实现类。 使用接口类&#xff1a; 如果requiredType是一个接口&#xff0c…...

自然语言处理(NLP)—— 自动摘要

自动摘要是一种将长文本信息浓缩为短文本的技术&#xff0c;旨在保留原文的主要信息和意义。 1 自动摘要的第一种方法 它的第一种方法是基于理解的&#xff0c;受认知科学和人工智能的启发。 在这个方法中&#xff0c;我们首先建立文本的语义表示&#xff0c;这可以理解为文本…...

Spring RestClient报错:400 Bad Request : [no body]

我项目采用微服务架构&#xff0c;所以各服务之间通过Spring RestClient远程调用&#xff0c;本来一直工作得好好的&#xff0c;昨天突然发现远程调用一直报错&#xff0c;错误详情如下&#xff1a; org.springframework.web.client.HttpClientErrorException$BadRequest: 400…...

【数据结构】 -- 堆 (堆排序)(TOP-K问题)

引入 要学习堆&#xff0c;首先要先简单的了解一下二叉树&#xff0c;二叉树是一种常见的树形数据结构&#xff0c;每个节点最多有两个子节点&#xff0c;通常称为左子节点和右子节点。它具有以下特点&#xff1a; 根节点&#xff08;Root&#xff09;&#xff1a;树的顶部节…...

C#面:XML与 HTML 的主要区别是什么

C# XML与HTML有以下几个主要区别&#xff1a; 用途不同&#xff1a;XML&#xff08;eXtensible Markup Language&#xff09;是一种用于存储和传输数据的标记语言&#xff0c;它的主要目的是描述数据的结构和内容。HTML&#xff08;HyperText Markup Language&#xff09;是一…...

java并发-如何保证线程按照顺序执行?

【readme】 使用只有单个线程的线程池&#xff08;最简单&#xff09;Thread.join() 可重入锁 ReentrantLock Condition 条件变量&#xff08;多个&#xff09; &#xff1b; 原理如下&#xff1a; 任务1执行前在锁1上阻塞&#xff1b;执行完成后在锁2上唤醒&#xff1b;任务…...

PyCharm中 Fitten Code插件的使用说明一

一. 简介 Fitten Code插件是是一款由非十大模型驱动的 AI 编程助手&#xff0c;它可以自动生成代码&#xff0c;提升开发效率&#xff0c;帮您调试 Bug&#xff0c;节省您的时间&#xff0c;另外还可以对话聊天&#xff0c;解决您编程碰到的问题。 前一篇文章学习了 PyCharm…...

Polar Web【简单】PHP反序列化初试

Polar Web【简单】PHP反序列化初试 Contents Polar Web【简单】PHP反序列化初试思路EXP手动脚本PythonGo 运行&总结 思路 启动环境&#xff0c;显示下图中的PHP代码&#xff0c;于是展开分析&#xff1a; 首先发现Easy类中有魔术函数 __wakeup() &#xff0c;实现的是对成员…...

树莓派4B 零起点(二) 树莓派 更换软件源和软件仓库

目录 一、准备工作&#xff0c;查看自己的树莓派版本 二、安装HTTPS支持 三、更换为清华源 1、更换Debian软件源 2&#xff0c;更换Raspberrypi软件仓库 四、进行软件更新 接前章&#xff0c;我们的树莓派已经启动起来了&#xff0c;接下来要干的事那就是更换软件源和软件…...

Pytorch 实现目标检测二(Pytorch 24)

一 实例操作目标检测 下面通过一个具体的例子来说明锚框标签。我们已经为加载图像中的狗和猫定义了真实边界框&#xff0c;其中第一个 元素是类别&#xff08;0代表狗&#xff0c;1代表猫&#xff09;&#xff0c;其余四个元素是左上角和右下角的(x, y)轴坐标&#xff08;范围…...

如何使用Python中的列表解析(list comprehension)进行高效列表操作

Python中的列表解析&#xff08;list comprehension&#xff09;是一种创建列表的简洁方法&#xff0c;它可以在单行代码中执行复杂的循环和条件逻辑。列表解析提供了一种快速且易于阅读的方式来生成新的列表。 以下是一些使用列表解析进行高效列表操作的示例&#xff1a; 1.…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

基于数字孪生的水厂可视化平台建设:架构与实践

分享大纲&#xff1a; 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年&#xff0c;数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段&#xff0c;基于数字孪生的水厂可视化平台的…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

SpringTask-03.入门案例

一.入门案例 启动类&#xff1a; package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...