如何用promsie实现观察者模式
发表于:2025-02-09 作者:千家信息网编辑
千家信息网最后更新 2025年02月09日,这篇文章主要介绍"如何用promsie实现观察者模式",在日常操作中,相信很多人在如何用promsie实现观察者模式问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"如何用
千家信息网最后更新 2025年02月09日如何用promsie实现观察者模式
![](https://www.aqdb.cn/uploadfile/d9/4913317c.jpg)
我们看到狗叫的动作是依赖小偷的,如果小偷不行动,狗是不会叫的,也就是说狗的叫的状态依赖小偷的行动,小偷的行动状态发生变化,依赖小偷的狗都会受到影响,从而发出叫声。
这个场景用代码来展示的话如下: 上面的代码中,小偷调用action方法的时候,其内部会分别调用每条狗的call方法。这段代码有个明显的缺点,对象耦合,不方便维护,假如需求中增加了一条狗,此时如何更改代码呢?代码如下: 观察代码,我们增加了dog4,然后在小偷的action方法中,再增加don4.call的调用,对象间存在了互相调用的耦合,这样的代码非常不便于后期维护,因为每次增加dog都需要去更改thief的代码。
那有没有另外一种代码的书写方式,增加dog,但是不修改thief的代码,同样达到上面的效果呢?
下面我们用观察者模式来改写这段代码,在改写之前,先来了解一下观察者模式的特点,我们再次回顾一下文中对观察者模式的介绍:"观察者模式定义了一种依赖关系,当某一个对象的状态发生变化,其它依赖这个对象的对象都会受到影响"。
仔细阅读我们发现观察者模式中一般会存在观察者和被观察者,通常被观察者是少数一方(并不固定,为了方便先这样理解)。
上面的例子中,小偷是少数一方,只有一个。小偷明显是被观察者,狗是观察者,被观察者通常会有两个方法和一个属性,一个方法叫做subscribe,这个方法用来收集观察者或者观察者的行为,另外一个方法叫做publish,用来发布消息,还有一个属性list,这个属性通常是一个数组,用来存储观察者或者观察者的行为。
下面我们用观察者模式来改写上面的代码,代码如下: 仔细阅读代码,我们首先重新定义了Thief类,并为其添加了subscribe方法、publish方法、list属性,并重新定义了dog。然后我们用thief的subscribe方法收集dog的call方法,将其添加到小偷的list属性中。当小偷调用action时,其内部调用publish方法,publish会遍历执行list数组中的方法。
这段代码相较于上一段代码就比较方便维护了,假如我们在这个基础上再添加一条狗,代码如下: 我们看到,代码中第41行增加dog4,然后调用thief的scrible收集狗的call方法,此时我们调用thief的publish方法,依然能调用所有dog的call方法,但是我们没有修改thief内部的代码,非常优雅的完成了需求,但是如果需求是再增加一个小偷呢?此时代码是什么样的呢?代码如下: 看看代码,我们在第30行新增了thief1对象,然后分别在第35、39、43行调用thief1的subsctible方法收集dog的call方法。
真是按下葫芦起了瓢,能不能继续优化呢,在使用观察者模式的时候,我们可以将观察者模式抽离出来,抽离成一个pubsub对象,这个对象有拥有两个方法一个属性,代码如下: 仔细阅读源码,我们只是将观察者的一个属性和两个方法抽离出来封装成了一个类,使用这个类时,实例化一下就可以了,然后用这个对象改写上面的代码: 观察代码,小偷在调用action时,不是直接调用狗的call方法,而是通过pubsub,并且收集狗的call方法,也是由pubsub来完成,完全将小偷和狗解耦了。然后我们在添加一个dog4和一个thief1,代码如下:
仔细阅读源码,第20行和第30行分别添加了thief1和dog4,依然能够实现小偷偷东西,狗会叫的功能,并且不会去修改thief和dog内部的代码,实现了对象之间的解耦。
观察者模式也可以叫做订阅发布模式,本质是一种消息机制,用这种机制我们可以解耦代码中对象互相调用。
第三版代码,我们可以用如下图示来理解: 观察上图,第三版中图片第一张图多了一个pubsub,我们用一个卫星来代替pubsub,这个版本也比较好维护,添加删除thief或者dog都不会影响到对象。我们在前端应用中使用的redux和vuex都运用了观察者模式,或者叫做订阅者模式,其运行原理也如上图。
文章写到这里,观察者模式基本就聊完了,但是我在观察pubsub这个对象的时候突然想到了promsie,promise天生就是观察者模式,我们可以用promise来改造一下pubsub,代码如下: Promise天然支持观察者模式,我们将其改造一下,改造成一个Pubsub类,与我们前面实现的Pubsub类效果是一样的。
首先我们在构造函数内部实例化一个promise,并且将这个promsie的resolve的控制权转交到this的resolve属性上。前面写过一篇文章 如何取消promise的调用 ,在这篇文章中我们介绍了如何获取promise的控制权。大家有兴趣可以去看一看。
回归正题,我们用promise改写的pubsub来测试下上面的案例,代码如下: 测试代码,我们发现用promise改造的pubsub也能很好的实现观察者模式,这里我们利用了promise的两个知识点,一个是promise的then方法,then方法可以无限追加函数。另外一个是我们得到promise的resolve的控制权,从而控制promise的then链的执行时机。
讲到这里填一下前面文章挖的坑,前面的 如何取消ajax请求的回调 的文章中我们留了一个坑,axios实现取消ajax请求的回调的原理,我们可以回顾下使用axios时如何取消回调,代码如下:
阅读代码,在第一步和第二步中,我们通过调用axios.CancelToken.source方法得到了一个source对象,第三步中我们在axios调用异步请求时传递cancelToken参数,第四步,在合适的时机调用source.cancle方法取消回调。
我们先看一下CancelToken这个静态方法的代码是如何的: 为了直观一些我们将注释和一些基础条件判断去除后,代码如下: 阅读源码,我们发现CancelToken是一个类,其构造函数需要传递一个参数,这个参数必须是一个函数,CancelToken通过调用source方法来实例化一个对象。
在CancelToken的构造函数中,实例化一个Promise对象,通过在Promise的外部定义ResolvePromise变量,值实例化promise的时候获取了Promise实例resolve的控制权,然后将控制权封装到cancel函数中,在将cancel函数交给CancelToken构造函数的参数executor函数。
CancelToken在调用cancel方法时,先实例化CancelToken,在实例化过程中,我们将cancel交给了变量cancel,最后将CancelToken的实例token和cancel方法返回出去。
token的实质就是一个promise对象,而cancel方法内部则保存了这个promise的resolve方法。所有我们可以通过cancel来控制promise对象的执行。
接着我们再看一下axios中配置cancelToken参数的核心代码: 阅读源码,我们发现,当axios发送异步请求配置了acncelToken参数后,axios内部会执行一段代码: 这段代码会调用传入的axios的cancelToken的promise.then的执行,但是这个promise.then的执行的控制权在cancel函数中,如果我们在这个异步请求的返回前,我们调用了cancle函数就会执行promise.then从而执行request.abort来取消回调。
axios取消异步回调的原理涉及到了两个知识点,首先是利用了xmlhttprequest的abort方法修改readystate的值,其次利用了观察值模式,只不过这个观察者模式用的是promise来实现的。
这篇文章主要介绍"如何用promsie实现观察者模式",在日常操作中,相信很多人在如何用promsie实现观察者模式问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"如何用promsie实现观察者模式"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
下面我们用一个示例来演示一下什么是观察者模式,有这样一个场景,在一个院子里,有一个小偷,和若干条狗,小偷只要一行动,狗就会叫,这个场景如果用图来展示的话如图:
![](https://www.aqdb.cn/uploadfile/d9/4913317c.jpg)
// 第一版
class Thief {
constructor(){
}
// thief的方法,调用dog的方法;
action(){
dog1.call()
dog2.call()
dog3.call()
}
}
class Dog {
call(){
console.log("狗叫")
}
}
let dog1 = new Dog()
let dog2 = new Dog()
let dog3 = new Dog()
let thief = new Thief();
thief.action()
// 第一版-新增dog4
class Thief {
constructor() {
}
// thief的方法,调用dog的方法;
action() {
dog1.call()
dog2.call()
dog3.call()
// 新增代码
dog4.call()
}
}
class Dog {
call() {
console.log("狗叫")
}
}
let dog1 = new Dog()
let dog2 = new Dog()
let dog3 = new Dog()
// 新增代码:
let dog4 = new Dog()
let thief = new Thief();
thief.action()
// 第二版
// 1、thief增加了list属性,是一个数组
// 2、subscrible方法,追加方法
// 3、publish 发布消息
class Thief {
constructor() {
this.list = []
}
//
subscrible(call) {
this.list.push(call)
}
// publish遍历数组,调用所有方法。
publish() {
for (let i = 0; i < this.list.length; i++) {
this.list[i]()
}
}
// thief的方法内部不会直接调用dog的方法了,
// 而是调用publish
action() {
this.publish()
}
}
class Dog {
call() {
console.log("狗叫")
}
}
let thief = new Thief();
let dog1 = new Dog()
thief.subscrible(dog1.call)
// 每增加一条狗就将狗的call方法追加到list
let dog2 = new Dog()
thief.subscrible(dog2.call)
let dog3 = new Dog()
thief.subscrible(dog3.call)
thief.action()
// 第二版,新增dog4
// 1、thief增加了list属性,是一个数组
// 2、subscrible方法,追加方法
// 3、publish 发布消息
class Thief {
constructor() {
this.list = []
}
//
subscrible(call){
this.list.push(call)
}
// publish遍历数组,调用所有方法。
publish(){
for(let i= 0 ;i
this.list[i]()
}
}
// thief的方法内部不会直接调用dog的方法了,
// 而是调用publish
action() {
this.publish()
}
}
class Dog {
call() {
console.log("狗叫")
}
}
let thief = new Thief();
let dog1 = new Dog()
thief.subscrible(dog1.call)
// 每增加一条狗就将狗的call方法追加到list
let dog2 = new Dog()
thief.subscrible(dog2.call)
let dog3 = new Dog()
thief.subscrible(dog3.call)
// 增加代码:
let dog4 = new Dog()
thief.subscrible(dog4.call)
thief.action()
// 第二版,新增thief
class Thief {
constructor() {
this.list = []
}
//
subscrible(call){
this.list.push(call)
}
// publish遍历数组,调用所有方法。
publish(){
for(let i= 0 ;i
this.list[i]()
}
}
// thief的方法内部不会直接调用dog的方法了,
// 而是调用publish
action() {
this.publish()
}
}
class Dog {
call() {
console.log("狗叫")
}
}
let thief = new Thief();
// 新增thief代码
let thief1 = new Thief()
let dog1 = new Dog()
thief.subscrible(dog1.call)
// 新增代码
thief1.subscrible(dog1.call)
let dog2 = new Dog()
thief.subscrible(dog2.call)
// 新增代码
thief1.subscrible(dog2.call)
let dog3 = new Dog()
thief.subscrible(dog3.call)
// 新增代码
thief1.subscrible(dog3.call)
thief.action()
// 新增代码
thief1.action()
class Pubsub{ constructor(){ this.list = [] } subscrible(call){ this.list.push(call) } publish(){ for(let i= 0 ;i
let pubsub = new Pubsub();
class Dog {
call() {
console.log("狗叫")
}
}
class Thief {
constructor() {
}
action() {
pubsub.publish()
}
}
let thief = new Thief();
let dog1 = new Dog()
pubsub.subscrible(dog1.call)
let dog2 = new Dog()
pubsub.subscrible(dog2.call)
let dog3 = new Dog()
pubsub.subscrible(dog3.call)
thief.action()
let pubsub = new Pubsub();
class Dog {
call() {
console.log("狗叫")
}
}
class Thief {
constructor() {
}
action() {
pubsub.publish()
}
}
let thief = new Thief();
// 新增thief1代码
let thief1 = new Thief();
let dog1 = new Dog()
pubsub.subscrible(dog1.call)
let dog2 = new Dog()
pubsub.subscrible(dog2.call)
let dog3 = new Dog()
pubsub.subscrible(dog3.call)
// 新增dog4代码
let dog4 = new Dog()
pubsub.subscrible(dog4.call)
thief.action()
class Pubsub { constructor() { let promise = new Promise((resolve,reject)=>{ this.resolve = resolve; }) this.promise = promise; } subscrible(call) { this.promise.then(call) } publish() { this.resolve(); }}
class Pubsub {
constructor() {
let promise = new Promise((resolve,reject)=>{
this.resolve = resolve;
})
this.promise = promise;
}
subscrible(call) {
this.promise.then(call)
}
publish() {
this.resolve();
}
}
let pubsub = new Pubsub();
class Dog {
call() {
console.log("狗叫")
}
}
class Thief {
constructor() {
}
action() {
pubsub.publish()
}
}
let thief = new Thief();
// 新增thief1代码
let thief1 = new Thief();
let dog1 = new Dog()
pubsub.subscrible(dog1.call)
let dog2 = new Dog()
pubsub.subscrible(dog2.call)
let dog3 = new Dog()
pubsub.subscrible(dog3.call)
// 新增dog4代码
let dog4 = new Dog()
pubsub.subscrible(dog4.call)
thief.action()
const axios = require('axios')// 1、获取CancelTokenvar CancelToken = axios.CancelToken;// 2、生成sourcevar source = CancelToken.source();console.log(source.token)axios.get('/user/12345', {//get请求在第二个参数 // 3、注入source.token cancelToken: source.token}).catch(function (thrown) { console.log(thrown)});axios.post('/user/12345', {//post请求在第三个参数 name: 'new name'}, { cancelToken: source.token}).catch(e => { console.log(e)});// 4、调用source.cancel("原因"),终止注入了source.token的请求source.cancel('不想请求了');
'use strict';var Cancel = require('./Cancel');/** * A `CancelToken` is an object that can be used to request cancellation of an operation. * * @class * @param {Function} executor The executor function. */function CancelToken(executor) { if (typeof executor !== 'function') { throw new TypeError('executor must be a function.'); } var resolvePromise; this.promise = new Promise(function promiseExecutor(resolve) { resolvePromise = resolve; }); var token = this; executor(function cancel(message) { if (token.reason) { // Cancellation has already been requested return; } token.reason = new Cancel(message); resolvePromise(token.reason); });}/** * Throws a `Cancel` if cancellation has been requested. */CancelToken.prototype.throwIfRequested = function throwIfRequested() { if (this.reason) { throw this.reason; }};/** * Returns an object that contains a new `CancelToken` and a function that, when called, * cancels the `CancelToken`. */CancelToken.source = function source() { var cancel; var token = new CancelToken(function executor(c) { cancel = c; }); return { token: token, cancel: cancel };};module.exports = CancelToken;
function CancelToken(executor) {
var resolvePromise;
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve;
});
var token = this;
executor(function cancel(message) {
if (token.reason) {
return;
}
token.reason = message
resolvePromise(token.reason);
});
}
CancelToken.source = function source() {
var cancel;
var token = new CancelToken(function executor(c) {
cancel = c;
});
return {
token: token,
cancel: cancel
};
};
if (config.cancelToken) { // Handle cancellation config.cancelToken.promise.then(function onCanceled(cancel) { if (!request) { return; } request.abort(); reject(cancel); // Clean up request request = null; });}
config.cancelToken.promise.then(function onCanceled(cancel) { if (!request) { return; } request.abort(); reject(cancel); // Clean up request request = null;});
到此,关于"如何用promsie实现观察者模式"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!
方法
代码
观察
观察者
模式
对象
小偷
函数
属性
实例
参数
控制
数组
控制权
面的
两个
文章
时候
消息
源码
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
软件开发涉及到哪些成本
建设银行 软件开发待遇
服务器中病毒怎么查询
我的世界沙雕服务器群
找网络安全专家
服务器的管理网口在哪里
数据库显示页面快捷键
山西现代化软件开发报价
网络游戏服务器
芝罘区微信小程序软件开发企业
腾讯云买服务器安全吗
小学生网络安全动漫画
数据库备份与恢复方案
网络技术的总结
软件开发立项报告 如何写
使用服务器搭建云盘的容量
国家网络安全 手抄报
网络技术应用 建立主题网站
济宁网络安全领域
服务器如何提供服务
网络安全检查为民办实事
魔兽怀旧服如何创建服务器
软件开发扩大使用收费
红警ol服务器满
网络安全的简图
数据库怎么简单查询
杭州科技互联网
芝罘区微信小程序软件开发企业
药业数据库
宝塔修改888数据库