js 发布订阅模式应用场景 自己手写一个简易的发布订阅模块

矫情吗;* 2022-10-04 15:46 252阅读 0赞

在这里插入图片描述

我们先写一个简单的发布订阅:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>发布订阅模式</title>
  7. </head>
  8. <body>
  9. <script type="text/javascript">
  10. var shopObj = {} // 定义发布者 卖家对象
  11. shopObj.list = [] // 缓存列表 存放订阅函数
  12. <span class="token comment">// 添加订阅者</span>
  13. shopObj<span class="token punctuation">.</span><span class="token function-variable function">listen</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">fn</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  14. shopObj<span class="token punctuation">.</span>list<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>fn<span class="token punctuation">)</span>
  15. <span class="token punctuation">}</span>
  16. <span class="token comment">// 发布消息</span>
  17. shopObj<span class="token punctuation">.</span><span class="token function-variable function">trigger</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  18. <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> i<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">,</span> fn<span class="token punctuation">;</span> fn <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>list<span class="token punctuation">[</span>i<span class="token operator">++</span><span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  19. <span class="token function">fn</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> arguments<span class="token punctuation">)</span>
  20. <span class="token punctuation">}</span>
  21. <span class="token punctuation">}</span>
  22. <span class="token comment">// 添加第一个订阅者</span>
  23. shopObj<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">color<span class="token punctuation">,</span> size</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  24. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">订阅者1:颜色是</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${<!-- --></span>color<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">, 尺码是</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${<!-- --></span>size<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
  25. <span class="token punctuation">}</span><span class="token punctuation">)</span>
  26. <span class="token comment">// 添加第二个订阅者</span>
  27. shopObj<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">color<span class="token punctuation">,</span> size</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  28. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">订阅者2:颜色是</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${<!-- --></span>color<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">, again尺码是</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${<!-- --></span>size<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
  29. <span class="token punctuation">}</span><span class="token punctuation">)</span>
  30. <span class="token comment">// 发布第一个消息 本意是要通知第1个订阅者 橘黄色 42尺码的货到了</span>
  31. shopObj<span class="token punctuation">.</span><span class="token function">trigger</span><span class="token punctuation">(</span><span class="token string">'orange'</span><span class="token punctuation">,</span> <span class="token number">42</span><span class="token punctuation">)</span>
  32. <span class="token comment">// 发布第二个消息 本意是要通知第2个订阅者 黑色 39尺码的货到了</span>
  33. shopObj<span class="token punctuation">.</span><span class="token function">trigger</span><span class="token punctuation">(</span><span class="token string">'black'</span><span class="token punctuation">,</span> <span class="token number">39</span><span class="token punctuation">)</span>
  34. </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>


在这里插入图片描述

可以看到,已经可以订阅消息,并且发布消息,但是有个问题,就是发布消息的时候,把消息发送给所有订阅者了,怎么才能解决这个问题呢?

我们回忆一下,我们在使用发布订阅的时候,是不是这样使用的,要传入一个标识:
在这里插入图片描述
然后在触发时候,和订阅时候传入的标识对应上,是不是就能解决这个问题了

我们这样改造: 添加订阅时候,传入一个标识符key, 如果缓存列表里不存在对应的list[key],那么就创建一个对应的list[key]初始值是一个空数组[]

如果有的话,直接把回调函数push进入对应的list[key]
在这里插入图片描述
完整代码:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>发布订阅模式2-优化</title>
  7. </head>
  8. <body>
  9. <script type="text/javascript">
  10. var shopObj = {} // 定义发布者 卖家对象
  11. shopObj.list = [] // 缓存列表 存放订阅函数
  12. <span class="token comment">// 添加订阅者</span>
  13. shopObj<span class="token punctuation">.</span><span class="token function-variable function">listen</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">key<span class="token punctuation">,</span> fn</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  14. <span class="token comment">// 没有对应key的话就创建一个数组</span>
  15. <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">this</span><span class="token punctuation">.</span>list<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  16. <span class="token keyword">this</span><span class="token punctuation">.</span>list<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
  17. <span class="token punctuation">}</span>
  18. <span class="token comment">// 然后再把回调函数push进list[key]</span>
  19. shopObj<span class="token punctuation">.</span>list<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>fn<span class="token punctuation">)</span>
  20. <span class="token punctuation">}</span>
  21. <span class="token comment">// 发布消息</span>
  22. shopObj<span class="token punctuation">.</span><span class="token function-variable function">trigger</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  23. <span class="token comment">// 获取对应的key</span>
  24. <span class="token keyword">var</span> key <span class="token operator">=</span> <span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">shift</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>arguments<span class="token punctuation">)</span> <span class="token comment">// 这里因为arguments是一个类数组 没有shift方法 需要借用</span>
  25. <span class="token keyword">var</span> fns <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>list<span class="token punctuation">[</span>key<span class="token punctuation">]</span>
  26. <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>fns <span class="token operator">||</span> fns<span class="token punctuation">.</span>length <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  27. <span class="token keyword">return</span>
  28. <span class="token punctuation">}</span>
  29. <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> i<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">,</span> fn<span class="token punctuation">;</span> fn <span class="token operator">=</span> fns<span class="token punctuation">[</span>i<span class="token operator">++</span><span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  30. <span class="token function">fn</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> arguments<span class="token punctuation">)</span>
  31. <span class="token punctuation">}</span>
  32. <span class="token punctuation">}</span>
  33. <span class="token comment">// 添加第一个订阅者dean</span>
  34. shopObj<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token string">'dean'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">color<span class="token punctuation">,</span> size</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  35. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">订阅者dean:颜色是</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${<!-- --></span>color<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">, 尺码是</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${<!-- --></span>size<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">的鞋子到货了!</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
  36. <span class="token punctuation">}</span><span class="token punctuation">)</span>
  37. <span class="token comment">// 添加第二个订阅者jing</span>
  38. shopObj<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token string">'jing'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">color<span class="token punctuation">,</span> size</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  39. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">订阅者jing:颜色是</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${<!-- --></span>color<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">, again尺码是</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${<!-- --></span>size<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">的鞋子到货了!</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
  40. <span class="token punctuation">}</span><span class="token punctuation">)</span>
  41. <span class="token comment">// 发布第一个消息 通知第dean 橘黄色 42尺码的鞋子到货了</span>
  42. shopObj<span class="token punctuation">.</span><span class="token function">trigger</span><span class="token punctuation">(</span><span class="token string">'dean'</span><span class="token punctuation">,</span> <span class="token string">'orange'</span><span class="token punctuation">,</span> <span class="token number">42</span><span class="token punctuation">)</span>
  43. <span class="token comment">// 发布第二个消息 通知第jing 黑色 39尺码的货到了</span>
  44. shopObj<span class="token punctuation">.</span><span class="token function">trigger</span><span class="token punctuation">(</span><span class="token string">'jing'</span><span class="token punctuation">,</span> <span class="token string">'black'</span><span class="token punctuation">,</span> <span class="token number">39</span><span class="token punctuation">)</span>
  45. </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>


/pre>

在这里插入图片描述

我们优化一下代码,再添加一个取消订阅:

  1. var event = {
  2. list: [],
  3. listen: function(key, fn) {
  4. // 没有对应key的话就创建一个数组
  5. if (!this.list[key]) {
  6. this.list[key] = []
  7. }
  8. // 然后再把回调函数push进list[key]
  9. shopObj.list[key].push(fn)
  10. },
  11. trigger: function() {
  12. // 获取对应的key
  13. var key = Array.prototype.shift.call(arguments)
  14. var fns = this.list[key]
  15. if (!fns || fns.length === 0) {
  16. return
  17. }
  18. <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> fn<span class="token punctuation">;</span> fn <span class="token operator">=</span> fns<span class="token punctuation">[</span>i<span class="token operator">++</span><span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  19. <span class="token function">fn</span><span class="token punctuation">(</span><span class="token operator">...</span>arguments<span class="token punctuation">)</span>
  20. <span class="token punctuation">}</span>

},
// 取消订阅
remove: function(key, fn) {
var fns = this.list[key]
if (!fns) {
return false
} else {
for (var i = fns.length - 1; i >= 0; i–) {
var _fn = fns[i]
if (_fn == fn) {
fns.splice(i, 1)
}
}
}
}
}

代码还可以用立即执行函数封装一下:

  1. // 代码封装成立即执行函数
  2. var Event = (function() {
  3. var list = [],
  4. listen,
  5. trigger,
  6. remove;
  7. listen = function(key, fn) {
  8. // 没有对应key的话就创建一个数组
  9. if (!this.list[key]) {
  10. this.list[key] = []
  11. }
  12. // 然后再把回调函数push进list[key]
  13. shopObj.list[key].push(fn)
  14. },
  15. trigger = function() {
  16. // 获取对应的key
  17. var key = Array.prototype.shift.call(arguments)
  18. var fns = this.list[key]
  19. if (!fns || fns.length === 0) {
  20. return
  21. }
  22. <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> fn<span class="token punctuation">;</span> fn <span class="token operator">=</span> fns<span class="token punctuation">[</span>i<span class="token operator">++</span><span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  23. <span class="token function">fn</span><span class="token punctuation">(</span><span class="token operator">...</span>arguments<span class="token punctuation">)</span>
  24. <span class="token punctuation">}</span>
  25. <span class="token punctuation">}</span><span class="token punctuation">,</span>
  26. <span class="token function-variable function">remove</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">key<span class="token punctuation">,</span> fn</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  27. <span class="token keyword">var</span> fns <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>list<span class="token punctuation">[</span>key<span class="token punctuation">]</span>
  28. <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>fns<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  29. <span class="token keyword">return</span> <span class="token boolean">false</span>
  30. <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span>
  31. <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> i <span class="token operator">=</span> fns<span class="token punctuation">.</span>length <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span> i <span class="token operator">>=</span> <span class="token number">0</span><span class="token punctuation">;</span> i<span class="token operator">--</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  32. <span class="token keyword">var</span> _fn <span class="token operator">=</span> fns<span class="token punctuation">[</span>i<span class="token punctuation">]</span>
  33. <span class="token keyword">if</span> <span class="token punctuation">(</span>_fn <span class="token operator">==</span> fn<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  34. fns<span class="token punctuation">.</span><span class="token function">splice</span><span class="token punctuation">(</span>i<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span>
  35. <span class="token punctuation">}</span>
  36. <span class="token punctuation">}</span>
  37. <span class="token punctuation">}</span>
  38. <span class="token punctuation">}</span>

return {
listen,
trigger,
remove
}
})()

我们再看一下,发布订阅模式的使用场景: 解决异步调用中的强耦合问题:

在这里插入图片描述
在这里插入图片描述

这种强耦合代码,缺点是,修改一个地方,其他地方都要跟着一起修改

使用发布订阅模式修改:
在这里插入图片描述
在这里插入图片描述

我们接着在vue里面自己手写一个简易的发布订阅模块:

Main.vue

  1. <template>
  2. <div>
  3. <el-input type="text" v-model="name" placeholder="Input name"></el-input>
  4. <el-button type="primary" @click="handlePub">Pub</el-button>
  5. <Son />
  6. </div>
  7. </template>

Son.vue

  1. <template>
  2. <div>
  3. <h1>{ { content}}</h1>
  4. </div>
  5. </template>

pubsub2.js

  1. var Event = (function(){
  2. var list = [],
  3. listen,
  4. trigger,
  5. remove;
  6. listen = function (key, fn) {
  7. // 没有对应key的话就创建一个数组
  8. if (!list[key]) {
  9. list[key] = []
  10. }
  11. // 然后再把回调函数push进list[key]
  12. list[key].push(fn)
  13. },
  14. trigger = function () {
  15. // 获取对应的key
  16. var key = Array.prototype.shift.call(arguments)
  17. var fns = list[key]
  18. if (!fns || fns.length === 0) {
  19. return
  20. }
  21. <span class="token comment">// for (var i=0, fn; fn = fns[i++];) {<!-- --></span>
  22. <span class="token comment">// fn(...arguments)</span>
  23. <span class="token comment">// }</span>
  24. <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i<span class="token operator"><</span> fns<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  25. <span class="token keyword">var</span> fn <span class="token operator">=</span> fns<span class="token punctuation">[</span>i<span class="token punctuation">]</span>
  26. <span class="token function">fn</span><span class="token punctuation">(</span><span class="token operator">...</span>arguments<span class="token punctuation">)</span>
  27. <span class="token punctuation">}</span>

},
remove = function (key, fn) {
var fns = list[key]
if (!fns) {
return false
} else {
for (var i = fns.length - 1; i >= 0; i–) {
var _fn = fns[i]
if (_fn == fn) {
fns.splice(i, 1)
}
}
}
}
return {
listen,
trigger,
remove
}
})()

export default Event

在这里插入图片描述
效果:
在这里插入图片描述

转载:https://blog.csdn.net/dyw3390199/article/details/118651237?utm\_medium=distribute.pc\_feed\_v2.none-task-blog-personrec\_tag-6.pc\_personrecdepth\_1-utm\_source=distribute.pc\_feed\_v2.none-task-blog-personrec\_tag-6.pc\_personrec

发表评论

表情:
评论列表 (有 0 条评论,252人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Redis发布订阅模式

    当使用银行卡消费的时候,银行往往会通过微信、短信或邮件通知用户这笔交易的信息,这便是一种发布订阅模式,这里的发布是交易信息的发布,订阅则是各个渠道。这在实际工作中十分常用,Re

    相关 redis发布/订阅模式

    大家都知道Redis中的list结构可以作为队列来满足一些生产消费的业务场景。实际上Redis还提供了发布/订阅(publish/subscribe)模式来实现类似的生产消费的