Often, we use design patterns in programming without even knowing that they exist. The publish/subscribe pattern is one of them. I'm pretty sure that every web developer has to work with the DOM and has to listen for events like click
or load
. Whenever we use addEventListener
, we use the publish/subscribe pattern.
The idea here is that we have objects (subscribers) that need some information and other objects that are ready to provide it (publishers). The pattern implements a mechanism/channel between them so they could communicate via messages. The message is often called event.
const Bus = {
_subscribers_: {},
subscribe(type, callback) {
if (!this._subscribers_[type]) this._subscribers_[type] = [];
this._subscribers_[type].push(callback);
return () => {
this._subscribers_[type] = this._subscribers_[type].filter((c) => c !== callback);
};
},
dispatch(type, payload) {
if (this._subscribers_[type]) {
this._subscribers_[type].forEach((c) => c(payload));
}
},
};
If you read the chapter for the Flux architecture, you will recognize a similar API. We need a local variable to keep the subscribers and a method for dispatching events.
const unsubscribe = Bus.subscribe("hey", (data) => {
console.log(`Hello, ${data.name}`);
});
Bus.dispatch("hey", { name: "Alan" }); // Hello, Alan
Bus.dispatch("hey", { name: "Margie" }); // Hello, Margie
unsubscribe();
Bus.dispatch("hey", { name: "George" }); // nothing
A typical characteristic of this pattern is organizing the events into topics. We pass the topic via the type
argument in our example. This way, we have a bucket of publishers and subscribers without having conflicts.
There is also usually an unsubscribing functionality. The subscribe
method returns a function that effectively removes the listener in the code above.
The most significant advantage of this concept is also its weakness. The different parties are isolated from each other. Often the subscribe doesn't know who the sender is. It may look like a good side effect, but when the application grows, it becomes a problem. It is difficult to track data flows and debugging issues.