Introduction to the
The publisk-subscribe pattern, also known as the observer pattern, defines a one-to-many dependency in which all dependent objects are notified when an object’s state changes.
Recall ever
As a front-end developer, binding events to DOM nodes is a very frequent occurrence. For example:
document.body.addEventListener('click'.function () {
alert(2333);
},false); document.body.click(); // Simulate the click eventCopy the code
Here we subscribe to the Document. body click event, and when the body is clicked, it posts the message to the subscriber, popping up 2333. We can also add and remove subscribers at will, and when a message is published, all subscribers will receive the message.
document.body.addEventListener('click'.function () {
alert(11111);
},false);
document.body.addEventListener('click'.function () {
alert(222);
},false);
document.body.addEventListener('click'.function () {
alert(333);
},false); document.body.click(); // Simulate the click eventCopy the code
It’s worth noting that the manually triggered event here we just used document.body.click(); But it is better to use fireEvent in IE and dispatchEvent in standard browsers as follows:
let fireEvent = function (element,event) {
if (document.createEventObject) {
var evt = document.createEventObject();
return element.fireEvent('on'+event,evt);
}else{
var evt = document.createEvent('HTMLEvents');
evt.initEvent(event,true.true);
return element.dispatchEvent(evt);
}
}
document.addEventListener('shout'.function (event) {
alert('shout');
})
fireEvent(document,'shout');
Copy the code
Talk now
People’s Daily life is inseparable from all kinds of interpersonal negotiation, for example, you have a lot of friends, at this time you want to get married, to you as the publisher, open your address book, one by one to call each subscriber you want to get married news. In an abstraction, implementing the publish-subscribe model requires:
- Publisher (You)
- Cached lists (contacts, your friends subscribe to all your messages)
- When Posting a message, iterate through the cache list, triggering the callback function of each of the subscribers in turn (call each one).
- In addition, the callback function can also add many parameters, subscribers can receive these parameters, such as you will tell them the wedding time, location, etc., subscribers can receive the message after their own processing.
let yourMsg = {};
yourMsg.peopleList = [];
yourMsg.listen = function (fn) {
this.peopleList.push(fn);
}
yourMsg.triger = function () {
for(var i = 0,fn; fn=this.peopleList[i++];) { fn.apply(this,arguments); } } yourMsg.listen(function (name) {
console.log(`${name}I got your message. }) yourMsg.listen(function (name) {
console.log('ha ha');
})
yourMsg.triger('Joe');
yourMsg.triger('bill');
Copy the code
- The above is a simple publish-subscribe implementation, but we find that the subscriber receives every message posted by the publisher. If Leo is dark and doesn’t want to hear about your marriage, he only wants to hear about your bad news, such as your being fired, he will be happy. At this point, we need to add a key so that subscribers only subscribe to the news they are interested in.
let yourMsg = {};
yourMsg.peopleList ={};
yourMsg.listen = function (key,fn) {
if(! PeopleList [key] = []; this.peoplelist [key] = []; this.peoplelist [key] = []; } this.peopleList[key].push(fn); } yourMsg.triger =function () {
let key = Array.prototype.shift.call(arguments);
let fns = this.peopleList[key];
if(! FNS | | FNS. Length = = 0) {/ / not subscribe to returnreturn false;
}
for(var i=0,fn; fn=fns[i++];) { fn.apply(this,arguments); } } yourMsg.listen('marrgie'.function (name) {
console.log(`${name}Want to know you married '); }) yourMsg.listen('unemployment'.function (name) {
console.log(`${name}Want to know you are unemployed '); }) yourMsg.triger('marrgie'.'Joe');
yourMsg.triger('unemployment'.'bill');
Copy the code
- You need to publish, and everyone has a circle of friends and everyone needs to publish, so it’s important to separate publish-subscribe and put it in a single object, and install it dynamically for anyone who needs it.
var event = {
peopleList:[],
listen:function (key,fn) {
if(! PeopleList [key] = []; this.peoplelist [key] = []; this.peoplelist [key] = []; } this.peopleList[key].push(fn) }, trigger:function () {
let key = Array.prototype.shift.call(arguments);
let fns = this.peopleList[key];
if(! FNS | | FNS. Length = = 0) {/ / not subscribe to returnreturn false;
}
for(var i=0,fn; fn=fns[i++];) { fn.apply(this,arguments); } } } var installEvent =function (obj) {
for(var i inevent){ obj[i] = event[i]; }}let yourMsg = {};
installEvent(yourMsg);
yourMsg.listen('marrgie'.function (name) {
console.log(`${name}Want to know you married '); }) yourMsg.listen('unemployment'.function (name) {
console.log(`${name}Want to know you are unemployed '); }) yourMsg.trigger('marrgie'.'Joe');
yourMsg.trigger('unemployment'.'bill');
Copy the code
- Sometimes we need to cancel the subscribed events. For example, Li Si is your good friend, but because of one thing, you broke up with him and deleted him from your address book. Here, we add a remove method to the event.
remove:function (key,fn) {
var fns = this.clientList[key];
if(! fns){return false;
}
if(! fn){ fns && (fns.length=0) }else{
for (let index = 0; index < fns.length; index++) {
const _fn = fns[index];
if(_fn === fn){ fns.splice(index,1); }}}}Copy the code
Discussion of publish-subscribe order
What we usually see is subscribe before publish, but is this order necessary? The answer is not necessarily. If the publisher publishes a message first, but no subscribers have subscribed to the message yet, we can keep the message from disappearing into the universe. Just like QQ offline messages, offline messages are stored in the server, and the recipient will receive the message after the next login. In the same way, we can build a store offline event stack, when events are published, if haven’t subscribers subscribe to this event, we’ll leave the publish event action wrapped in a function, the packing function will be deposited in the stack, wait for the object to subscribe to the event, we will be packed traversal stack and in turn perform these functions, That is to resend the event inside, but the offline event life cycle is only once, just like QQ unread messages will only prompt you once.
JavaScript implements the convenience of a publish-subscribe model
Because JavaScript has the advantage of callback functions, it’s easier to write development-subscribe. Traditional publish-subscribe systems such as Java typically pass the subscriber itself as a reference to the publisher object, and the subscriber object provides a method called such as update that the publisher object can call when appropriate. The following code uses JS to simulate the traditional implementation.
function Dep() {
this.subs = [];
}
Dep.prototype.addSub = function (sub) {
this.subs.push(sub);
}
Dep.prototype.notify = function () {
this.subs.forEach(sub=>sub.update());
}
function Watcher(fn) {
this.fn = fn;
}
Watcher.prototype.update = function () {
this.fn();
}
var dep = new Dep();
dep.addSub(new Watcher(function () {
console.log('okokok');
}))
dep.notify();
Copy the code
summary
- Publish-subscribe has obvious advantages, such as decoupling time and decoupling between objects. In terms of architecture, MVC and MVVM are not without publish-subscribe participation. Vue we commonly use is also based on publish-subscribe. EventEmitter in Node is also published and subscriptionalized, and I’ve written an implementation of it before.
- There are also drawbacks to publish-subscribe. Creating a subscriber itself takes time and memory, and when you subscribe to a message, the message may never happen, but the subscriber will always be in memory. If a publish-subscribe program is heavily used, it can also make it difficult to track bugs.