当前位置: 首页 > 后端技术 > Node.js

JavaScript发布订阅模型

时间:2023-04-03 17:09:26 Node.js

发布订阅模型,看似奇怪,实则不然。工作中经常用到,比如Node.jsEventEmitter中的on和emit方法;Vue中的$on和$emit方法。它们都采用了发布-订阅的模式,让开发更加高效便捷。一、什么是发布订阅模型1、定义发布订阅模型其实就是对象之间的一种一对多的依赖关系。当一个对象的状态发生变化时,所有依赖于它的对象都会收到状态变化的通知。订阅者(Subscriber)向调度中心(EventChannel)注册(Subscribe)他要订阅的事件,当发布者(Publisher)向调度中心发布事件(PublishEvent),即事件被触发时、调度中心统一调度(FireEvent)订阅者注册到调度中心处理代码。2.比如我们喜欢看公众号上的某篇文章,但是又不知道什么时候会有新文章出,只好时不时看一遍;这个时候我们可以关注这个公众号,当有文章推送的时候,会有消息及时通知我们文章更新了。上面看似简单的操作,其实就是一个典型的发布-订阅模型。公众号属于发布者,用户属于订阅者;用户将订阅公众号的事件注册到调度中心,公众号作为发布者。当有新文章发布时,公众号将事件发布到调度中心,调度中心会及时发送消息通知用户。2.如何实现发布-订阅模型?1.实现思路创建一个对象,在对象上创建一个缓存列表(调度中心)。on方法用于将函数fn添加到缓存列表(订阅者注册事件到调度中心)。emit方法接受参数中的第一个。作为事件,根据事件值执行对应缓存列表中的函数(发布者将事件发布到调度中心,调度中心处理代码)。off方法可以根据事件值取消订阅(unsubscribe)。once方法只监听一次,调用Cache函数后删除(订阅一次)2.demo1先看一个简单的demo,实现了on和emit方法,代码中有详细注释。//公众号objectleteventEmitter={};//缓存列表,存储事件和fneventEmitter.list={};//订阅eventEmitter.on=function(event,fn){let_this=this;//如果对象中没有对应的事件值,也就是说没有订阅,则为该事件创建一个缓存列表//如果对象中有对应的事件值,则将fn添加到对应的事件中缓存列表(_this.list[event]||(_this.list[event]=[])).push(fn);return_this;};//发布eventEmitter.emit=function(){let_this=this;//第一个参数是对应的Event值,直接用数组的shift方法取出来letevent=[].shift.call(arguments),fns=[..._this.list[event]];//如果缓存列表中没有fn,则返回falseif(!fns||fns.length===0){returnfalse;}//遍历事件值对应的缓存列表,依次执行fnfns.forEach(fn=>{fn.apply(_this,arguments);});return_this;};functionuser1(content){console.log('User1subscribed:',content);};functionuser2(content){console.log('User2subscribed:',content);};//subscribeeventEmitter.on('article',user1);eventEmitter.on('article',user2);//publisheventEmitter.emit('article','Javascriptpublish-subscribereadmode');/*用户1订阅:Javascript发布-订阅模式用户2订阅:Javascript发布-订阅模式*/3.在这个版本的demo2中,我们添加了onceandoff方法leteventEmitter={//缓存列表list:{},//订阅(event,fn){let_this=this;//如果对象中没有对应的事件值,则表示没有订阅,为该事件创建一个缓存列表//如果对象中有对应的事件值,则将fn添加到对应的事件缓存列表中(_this.list[event]||(_this.list[event]=[])).push(fn);返回_this;},//Listenonce(event,fn){//先绑定,调用后删除let_this=this;函数on(){_this.off(event,on);fn.apply(_this,参数);}on.fn=fn;_this.on(事件,开启);返回_this;},//取消订阅(event,fn){let_this=this;让fns=_this.list[事件];//如果缓存列表中没有对应的fn,则返回falseif(!fns)returnfalse;if(!fn){//如果没有传入fn,则清除事件值对应的缓存列表中的所有fnfns&&(fns.length=0);}else{//如果有fn,遍历缓存链表,看传入的fn和哪个函数相同,如果相同,就从缓存链表中删除letcb;对于(让我=0,cbLen=fns.长度;我{fn.apply(_this,arguments);});返回_this;}};functionuser1(content){console.log('User1hassubscribed:',content);}functionuser2(content){console.log('User2subscribed:',content);}functionuser3(content){console.log('User3subscribed:',content);}functionuser4(content){console.log('User4hassubscribed:',content);}//subscribeeventEmitter.on('article1',用户1);eventEmitter.on('article1',user2);eventEmitter.on('article1',user3);//取消订阅user2方法eventEmitter.off('article1',user2);eventEmitter.once('article2',user4)//publisheventEmitter.emit('article1','Javascript发布-订阅模式');eventEmitter.emit('article1','Javascript发布-订阅模式');eventEmitter.emit('article2','Javascript观察者模式'');eventEmitter.emit('article2','Javascript观察者模式');//eventEmitter.on('article1',user3).emit('article1','test111');/*用户1已订阅:JavascriptPublish-订阅模型用户3订阅:Javascript发布-订阅模型用户1订阅:Javascript发布-订阅模型用户3订阅:Javascript发布-订阅模型用户4订阅:Javascript观察者模型*/三、Vue对发布-订阅模式的认识,下面看看Vue中如何实现$on和$emit,直接看源码:functioneventsMixin(Vue){varhookRE=/^hook:/;Vue.prototype.$on=function(event,fn){varthis$1=this;varvm=这个;//当event是数组时,循环$onif(Array.isArray(event)){for(vari=0,l=event.length;i1?toArray(cbs):cbs;//只取回调函数,不取回eventvarargs=toArray(arguments,1);对于(vari=0,l=cbs.length;i