Predecessor observer mode of publish / subscribe mode
Observer pattern defines a one to many dependency between objects. When the state of an object changes, all objects that depend on it will be notified and updated automatically. The observer model belongs to the behavioral model, which focuses on the communication between objects. The observer model is the communication between the observer and the observed.
The observer mode is called "publish subscribe mode", or "subscribe publish mode". Subscribers and subscription targets are linked together. When the subscription targets change, subscribers are notified one by one.
What is publish / subscribe mode
In fact, among the 24 basic design patterns, there is no publish subscribe pattern. As mentioned above, it is just another name for observer pattern.
But after time, it seems that he has become stronger, independent of the observer mode, and become a different design mode.
In the current publish and subscribe mode, the message sender called the publisher will not send the message directly to the subscriber, which means that the publisher and the subscriber do not know each other's existence. There is a third component between publishers and subscribers, called message broker or scheduling center or middleware. It maintains the relationship between publishers and subscribers, filters all incoming messages from publishers and distributes them to subscribers accordingly.
Fundamental role
- Widely used in asynchronous programming (instead of transfer callback function)
- Writing code with loose coupling between objects
Basic case introduction
Background: Chengdu's mother rabbit head is really fragrant. There are too many buyers who need to book to buy it, so the customer is the subscriber who subscribes to the mother rabbit head.
And mom's rabbit head needs to inform customers to buy when it's ready. Otherwise, she won't make any money. She needs to inform all subscribers to pick up rabbit head when it's ready. At this time, mom's rabbit head shop is the publisher.
/*Rabbit head shop*/ var shop={ listenList:[],//Cache list addlisten:function(fn){//Add subscribers this.listenList.push(fn); }, trigger:function(){//Publish news for(var i=0,fn;fn=this.listenList[i++];){ fn.apply(this,arguments); } } } /*Xiaoming subscribes to the store*/ shop.addlisten(function(taste){ console.log("Inform Xiaoming,"+taste+"It's delicious"); }); /*Bruce Lee subscribed to the store*/ shop.addlisten(function(taste){ console.log("Inform Bruce Lee,"+taste+"It's delicious"); }); /*Xiaohong subscribes to the store*/ shop.addlisten(function(taste){ console.log("Notice Xiaohong,"+taste+"It's delicious"); }); // Publish subscribe shop.trigger("Medium spicy");
//console Inform Xiao Ming that the spicy taste is good Inform Xiao Long that the spicy taste is good Inform Xiaohong that the spicy taste is good
Case escalation
There is a problem in the above case. Because all subscriptions are triggered when triggering, there is no distinction and judgment. Therefore, a Key is needed to distinguish the types of subscriptions and trigger according to different situations. And subscriptions can be cancelled.
Upgrading ideas:
- Create an object (cache list)
- The addlisten method is used to add all the subscription callback functions fn to the cache list listenList
- The trigger method takes the first one in arguments as the key, and executes the function in the corresponding cache list according to the key value
- The remove method can unsubscribe based on the key value
/*Rabbit head shop*/ var shop={ listenList:{},//Cache objects addlisten:function(key,fn){ // There is no key to give an initial value to avoid calling an error if (!this.listenList[key]) { this.listenList[key] = []; } // Add subscribers. A key is a subscription type this.listenList[key].push(fn); }, trigger:function(){ const key = Array.from(arguments).shift() const fns = this.listenList[key] // Two special cases are excluded here. The first one is triggered by a type that has never been subscribed. The second one cancels all subscribed if(!fns || fns.length===0){ return false; } // Publish a message, trigger all subscriptions of the same type, fns.forEach((fn)=>{ fn.apply(this,arguments); }) /* for(var i=0,fn;fn=fns[i++];){ fn.apply(this,arguments); } */ }, remove:function(key,fn){ var fns=this.listenList[key];//Get the corresponding message set of this type if(!fns){//If the corresponding key is not subscribed, return directly return false; } if(!fn){//If there is no specific return, all subscriptions need to be cancelled fns && (fns.length=0); }else{ for(var l=fns.length-1;l>=0;l--){//Traverse function list if(fn===fns[l]){ // This is a comparison of the incoming addresses, so you can't use anonymous functions directly fns.splice(l,1);//Delete subscriber fallback } } } } } function xiaoming(taste){ console.log("Inform Xiaoming,"+taste+"It's delicious"); } function xiaolong(taste){ console.log("Inform Bruce Lee,"+taste+"It's delicious"); } function xiaohong(taste){ console.log("Notice Xiaohong,"+taste+"It's delicious"); } // Xiaoming subscribes to the store shop.addlisten('Medium spicy',xiaoming); shop.addlisten('Extra spicy',xiaoming); // Bruce Lee subscribed to the store shop.addlisten('Slightly spicy',xiaolong); // Xiaohong subscribes to the store shop.addlisten('Medium spicy',xiaohong); // Xiaohong suddenly doesn't want to eat it shop.remove("Medium spicy",xiaohong); // Publish and subscribe to the spicy food shop.trigger("Medium spicy"); shop.trigger("Slightly spicy"); shop.trigger("Extra spicy");
On the order of publish and subscribe
What we usually see is subscription before publishing, but do we have to follow this order? The answer is not necessarily. If the publisher publishes a message first, but no subscriber has subscribed to the message at this time, we can prevent the message from disappearing into the universe. Just like the QQ offline message, the offline message is saved in the server, and the receiver will receive this message only after the next login. Similarly, we can build a stack to store offline events. When an event is published, if no subscriber subscribes to the event at this time, we temporarily wrap the action of publishing the event in a function, and these wrapper functions will be stored in the stack. When an object subscribes to the event, we will traverse the stack and execute these wrapper functions in turn, That is, resend the internal events, but the offline events have only one life cycle, just like QQ unread messages will only prompt you once.
Summary
The advantages of publish subscribe are obvious. It achieves the decoupling between time and objects. From the perspective of architecture, MVC and MVVM can't do without the participation of publish subscribe.
The eventemitters in the same node are also published and subscribed