The Publish-subscribe pattern is also called the Observer pattern, which defines a one-to-many dependency between objects, and when an object's state changes, all objects that depend on it are notified.
The publish-subscribe pattern can be widely used in asynchronous programming, which is an alternative to the transitive callback function.
Can replace the hard-coded notification mechanism between objects, and an object does not have to explicitly invoke an interface of another object.
Custom events
- Start by specifying who will act as the publisher;
- Then add a cache list to the publisher to hold the callback function to inform the subscribers;
- When the message is last published, the publisher iterates through the cache list, triggering the subscriber callback function that is placed inside.
Also, we can fill in some parameters in the callback function, and subscribers can receive these parameters.
The simplest publish-subscribe mode:
varSalesoffices = {};//Define sales OfficeSalesoffices.clientlist = [];//cache list, which holds subscribers ' callback functionsSalesoffices.listen =function(FN) {//Add Subscribers This. Clientlist.push (FN);//subscribed messages are added to the cache list }; Salesoffices.trigger=function(){//Release Mode for(varI=0,FN;FN = This. clientlist[i++]; ) {fn.apply ( This, arguments);//arguments is the parameter that is taken when the message is published } }; Salesoffices.listen (function(Price,squaremeter) {//xiaoming Subscription MessageConsole.log (' Price = ' +Price ); Console.log (' Squaremeter= ' +squaremeter); }); Salesoffices.listen (function(Price,squaremeter) {//Little Red Subscribe messageConsole.log (' Price = ' +Price ); Console.log (' Squaremeter= ' +squaremeter); }); Salesoffices.trigger (2000000,88);//output: 2 million, 88 sqmSalesoffices.trigger (3000000,110);//output: 3 million, 110 sqm
Let subscribers subscribe only to messages of interest to them:
varSalesoffices = {};//Define sales OfficeSalesoffices.clientlist = [];//cache list, which holds subscribers ' callback functionsSalesoffices.listen =function(KEY,FN) {if(! This. Clientlist[key]) {//If you have not subscribed to such a message, create a cache list for the class message This. Clientlist[key] = []; } This. Clientlist.push (FN);//subscribed messages are added to the cache list }; Salesoffices.trigger=function(){//Publish a message varKey = Array.prototype.shift.call (arguments),//Remove Message TypeFNS = This. Clientlist[key];//Remove the callback function collection for the message if(!fns | | fns.length ===0) {//If the message is not subscribed, it returns return false; }; for(varI=0,FN;FN = This. clientlist[i++]; ) {fn.apply ( This, arguments);//arguments is the parameter that comes with when you publish a message } }; Salesoffices.listen (' SquareMeter88 ',function(Price) {//Xiaoming subscribed to 88 messageConsole.log (' Price = ' +Price ); }); Salesoffices.listen (' SquareMeter110 ',function(Price) {//Little Red Subscription 110 messageConsole.log (' Price = ' +Price ); }); Salesoffices.trigger (' SquareMeter88 ', 88); Salesoffices.trigger (' squareMeter110 ', 110);
Common implementations:
//Publish-Subscribe feature varevent ={clientlist:[], listen:function(KEY,FN) {if(! This. Clientlist[key]) { This. Clientlist[key] = []; } This. Clientlist[key].push (FN);//subscribed messages are added to the cache list}, Trigger:function(){ varKey =Array.prototype.shift.call (arguments), FNS= This. Clientlist[key]; if(!fns | | fns.length ===0) {//if the corresponding message is not bound return false; } for(varI =0, FN;FN = fns[i++]; ) {fn.apply ( This. arguments);//arguments is the parameter on the trigger. } } };//dynamically installs the Publish-subscribe feature for all objects varInstallevent =function(obj) { for(varIinchevent) {Obj[i]=Event[i]; } }; varSalesoffices = {}; Installevent (salesoffices); Salesoffices.listen (' SquareMeter88 ',function(Price) {//xiaoming Subscription MessageConsole.log (' Price = ' +Price ); }); Salesoffices.listen (' SquareMeter110 ',function(Price) {//Little Red Subscribe messageConsole.log (' Price = ' +Price ); }); Salesoffices.trigger (' SquareMeter88 ', 2000000); Salesoffices.trigger (' squareMeter110 ', 3000000);
To unsubscribe from events:
//Unsubscribe from EventsEvent.remove =function(KEY,FN) {varFNS = This. Clientlist[key]if(!FNS) {//if the message corresponding to key is not subscribed to, it is returned directly return false; } if(!FN) {//if no specific callback function is passed, all subscriptions that need to cancel the key corresponding messagefns&& (fns.length = 0); }Else{ for(varL = fns.length-1;l>=0;l--) {//List of callback functions for reverse traversal subscriptions var_FN =Fns[l]; if(_FN = = =fn) {Fns.splice (L,1);//remove a subscriber's callback function } } } }; varSalesoffices = {}; varInstallevent =function(obj) { for(varIinchevent) {Obj[i]=Event[i]; }} installevent (Salesoffices); Salesoffices.listen (' SquareMeter88 ', fn1=function(Price) {//xiaoming Subscription MessageConsole.log (' Price = ' +Price ); }); Salesoffices.listen (' squareMeter110 ', fn2=function(Price) {//Little Red Subscribe messageConsole.log (' Price = ' +Price ); }); Salesoffices.remove (' SquareMeter88 ', FN1); Salesoffices.trigger (' squareMeter110 ', 2000000);
Global Publish-Subscribe mode:
The publish-subscribe pattern can be implemented with a global event object that the Subscriber does not need to know clearly from which publisher, the Publisher does not know which subscribers the message will be pushed to, and the event acts as a "mediator" role, linking subscribers and publishers.
varEvent = (function(){ varClientlist ={}, listen, trigger, remove; Listen=function(KEY,FN) {if(!Clientlist[key]) {Clientlist[key]= []; } clientlist[key].push (FN); }; Trigger=function(){ varKey =Array.prototype.shift.call (arguments), FNS=Clientlist[key]; if(!fns| | Fns.length===0){ return false; } for(vari=0,fn;fn=fns[i++]; ) {fn.apply ( This, arguments); } }; Remove=function(KEY,FN) {varFNS =Clientlist[key]; if(!FNS) { return false; } if(!fn) {FNS&& (fns.length = 0); }Else{ for(varl=fns.length-1;l>=0;l--){ var_FN =Fns[l]; if(_FN = = =fn) {Fns.splice (L,1); } } } }; return{listen:listen, Trigger:trigger, Remove:remove}}) (); Event.listen (' SquareMeter88 ',function(Price) {Console.log (' Price = ' +Price ); }); Event.trigger (' squareMeter88 ', 2000000);
Inter-module communication:
<! DOCTYPE html>varA = (function(){ varCount = 0; varbutton = document.getElementById (' Count '); Button.Click=function() {Event.trigger (' Add ', count++); } })(); varB = (function(){ vardiv = document.getElementById (' Show '); Event.listen (' Add ',function(count) {div.innerhtml=count; }); })();</script>
Do I have to subscribe and republish?
In some cases, we need to save this message first, and then post the message to the Subscriber when there is an object to subscribe to it.
Naming conflicts for global events:
Provides the event object with the ability to create a namespace.
In JavaScript, we replace the traditional publish-subscribe pattern with the form of a registered callback function, which is more elegant and simple.
The advantages of the publish-subscribe model are obvious, one-time decoupling, and two decoupling on the object.
JavaScript Design patterns and development Practices---reading notes (8) Publish-Subscribe mode