javaScript 观察者模式和发布订阅模式

观察者模式和发布订阅模式

基本概念

发布订阅模式

在这个模式中,发布者(或者说是主题)并不直接发送消息给订阅者,而是通过调度中心(或者叫消息代理)来传递消息。 发布者(或者说是主题)并不知道订阅者的存在,而订阅者也不知道发布者的存在。他们彼此唯一的关系就是在调度中心注册成为订阅者或者发布者。

观察者模式

观察者(Observer)模式中包含两种对象,分别是目标对象和观察者对象。在目标对象和观察者对象间存在着一种一对多的对应关系,当这个目标对象的状态发生变化时,所有依赖于它的观察者对象都会得到通知并执行它们各自特有的行为。

区别

简单来讲,观察者模式是基于发布订阅模式的,发布订阅模式需要手动进行订阅和发布,是基于中间的调度栈进行的,可以不发布,也可以不订阅,发布订阅是解偶没有关联的,而观察者模式是有相互依赖关系的,并且观察者必须实现一个update方法提供调用。

发布订阅者模式示例

  • 如下,有两个异步获取文件的函数,在不使用promise的情况下,需要在读取到两个文件后执行finish。
js 复制代码
const fs = require('fs'); // 引入node中的fs模块
fs.readFile('./1.text', 'utf8', function (error, data) {
 	console.log('读取到了文件1.text'); 
});
fs.readFile('./2.text', 'utf8', function (error, data) {
 	console.log('读取到了文件2.text'); 
});
function finish() {
  console.log('文件读取完成');
}
  • 利用发布订阅模式,实现一个中间调度栈event
  • event需要缓存发布的数据的数据和订阅回调函数
  • event需要实现on和emit方法提供发布和订阅功能
    • on接收一个函数作为参数,并且将该函数进行缓存,等待发布消息后按顺序执行。
    • emit接收key和value两个参数,作为发布的键值对数据进行缓存,订阅函数被执行是传入。
js 复制代码
const event = {
  _arr: [], // 用于缓存所有订阅的回调函数
  data: {}, // 用于缓存发布的数据
  on(fn) {
    // 直接缓存订阅函数
    this._arr.push(fn);
  },
  emit(key, value) {
    // 发布数据,直接缓存数据并且执行所有的订阅函数。
    this.data[key] = value;
    this._arr.forEach(fn => fn(this.data));
  }
}
  • 最后使用该调度栈进行简单的发布订阅
js 复制代码
const fs = require('fs'); // 引入node中的fs模块
const event = {
  _arr: [], // 用于缓存所有订阅的回调函数
  data: {}, // 用于缓存发布的数据
  on(fn) {
    // 直接缓存订阅函数
    this._arr.push(fn);
  },
  emit(key, value) {
    // 发布数据,直接缓存数据并且执行所有的订阅函数。
    this.data[key] = value;
    this._arr.forEach(fn => fn(this.data));
  }
}

event.on((data) => { // 订阅
  console.log('当前读取到的文件', data);
});
event.on((data) => { // 订阅
  if (Reflect.ownKeys(data).length === 2) { // 等价于Object.Keys()
    finish();
  }
});

fs.readFile('./1.text', 'utf8', function (error, data) {
 	console.log('读取到了文件1.text'); 
  event.emit('text1', '1111'); // 发布
});
fs.readFile('./2.text', 'utf8', function (error, data) {
 	console.log('读取到了文件2.text'); 
  event.emit('text2', '2222'); // 发布
});
function finish() {
  console.log('文件读取完成');
  event.emit(); // 发布
}

观察者模式

  • 观察者模式分为观察者被观察者两个类。
  • 被观察者需要接收观察者。
  • 观察者需要提供update方法。
js 复制代码
// 被观察者
class Subject {
  constructor() {
    this.observers = []; // 用于接收观察者
    this.state = false; // 当前的被观察者状态
  }
  attach(fn) {
    this.observers.push(fn); // 订阅模式,被观察者接收观察者
  }
  setState(newValue) {
    this.state = newValue; // 修改状态
   	// 当状态变化时,需要通知所有观察者,调用观察者的update方法.
    this.observers.forEach(fn => fn.update(newValue));
  }
}

// 观察者
class Observer {
  update() {
    console.log('收到了通知!');
  }
}

const a = new Subject();
const b = new Observer();
const c = new Observer();
a.attach(o1); // 订阅
a.attach(o2); // 订阅

// 修改被观察者状态
a.setState(false);
相关推荐
kyriewen9 分钟前
折腾了半年 AI 编程工作流,最后发现效率瓶颈是桌上那块屏幕
前端·javascript·ai编程
蜗牛前端37 分钟前
codex 全流程开发上线的高颜值礼簿小程序
前端·微信小程序
大龄秃头程序员1 小时前
我在图文流 App 里落地双层缓存、弱网降级与 OOM 治理
前端
老王以为1 小时前
React Renderer 分离的多平台架构
前端·react native·react.js
hunterandroid1 小时前
Kotlin Coroutines 与 Flow:让异步任务更清晰
前端
Bigger2 小时前
从零搭建 AI 代码审查服务:一份前端也能看懂的 Python 学习笔记
前端·ci/cd·ai编程
lichenyang4532 小时前
JSAPI、NAPI、Biz、Imp:ASCF Demo 如何真正调用系统能力和 C++ 能力
前端
lichenyang4533 小时前
IPC、JSVM、UIThread、libuv:ASCF 架构图里最容易混的几个词
前端
用户059540174463 小时前
Redis记忆存储故障恢复测试踩坑实录:手动测试让我漏掉了2个一致性Bug
前端·css
用户2136610035723 小时前
Vue2脚手架工程化与Axios集成
前端·vue.js