Design patterns in Javascript: Publish-Subscribe or PubSub

Search for a command to run...

No comments yet. Be the first to comment.
What is virtual scroll ? Virtual scroll (also called virtualization or windowing) is a technique used to efficiently render large lists in web applications — without loading everything into the DOM at once. Instead of rendering 1,000+ items, you only...

The graph data structure is a fundamental concept in computer science, and it is used in various domains such as social networks, transportation systems, and computer networks. A graph is a collection of nodes connected by edges, where each node repr...

So far we've looked at implementation of tree data structure and some of it's variants such as trie. In this post, we'll dive into heaps. These are also referred to as priority queues. Introduction Heap is a variant of tree data structure, with two a...

We've already covered the basics of tree data structure in three posts. If you haven't gone through those yet, I would strongly going through the introductory post at the very least. Introduction Trie is a variation of tree data structure. It's also ...

HTTP(Hyper Text Transfer Protocol) is one of many protocols used for transferring data (think of html pages, text, images, videos and much more) across machines. There are other application layer protocols like FTP, SMTP, DHCP etc. HTTP was invente...

What's a design pattern in software engineering? It's a general repeatable solution to a commonly occurring problem in software design. In this article, we'll be looking at one of such common design patterns and see how it can be put to use in real world applications.
This pattern is referred to as Publish-Subscribe or PubSub. Let's start with the overall notion behind this pattern before writing some code.

The image above describes the general idea behind this pattern:
subscribers (a subscriber is just a function)subscribe(subscriber) method, which essentially adds the subscriber into our PubSub containerpublish(payload) to call all the existing subscribers in the PubSub container with payloadsubscriber can be removed from the container, at any point in time, using the unsubscribe(subscriber) method.Looking at the points above it's pretty straightforward to come up with a simple implementation:
// pubsub.js
export default class PubSub {
constructor(){
// this is where we maintain list of subscribers for our PubSub
this.subscribers = []
}
subscribe(subscriber){
// add the subscriber to existing list
this.subscribers = [...this.subscribers, subscriber]
}
unsubscribe(subscriber){
// remove the subscriber from existing list
this.subscribers = this.subscribers.filter(sub => sub!== subscriber)
}
publish(payload){
// publish payload to existing subscribers by invoking them
this.subscribers.forEach(subscriber => subscriber(payload))
}
}
Let's add a bit of error handling to this implementation:
// pubsub.js
export default class PubSub {
constructor(){
this.subscribers = []
}
subscribe(subscriber){
if(typeof subscriber !== 'function'){
throw new Error(`${typeof subscriber} is not a valid argument for subscribe method, expected a function instead`)
}
this.subscribers = [...this.subscribers, subscriber]
}
unsubscribe(subscriber){
if(typeof subscriber !== 'function'){
throw new Error(`${typeof subscriber} is not a valid argument for unsubscribe method, expected a function instead`)
}
this.subscribers = this.subscribers.filter(sub => sub!== subscriber)
}
publish(payload){
this.subscribers.forEach(subscriber => subscriber(payload))
}
}
We can use this implementation as follows:
// main.js
import PubSub from './PubSub';
const pubSubInstance = new PubSub();
export default pubSubInstance
Now, elsewhere in the application, we can publish and subscribe using this instance:
//app.js
import pubSubInstance from './main.js';
pubSubInstance.subscribe(payload => {
// do something here
showMessage(payload.message)
})
// home.js
import pubSubInstance from './main.js';
pubSubInstance.publish({ message: 'Hola!' });
Yes. In fact, there are many libraries that use it under the hood and you may not have realized it so far. Let's take the example of the popular state management library for ReactJS - Redux. Of course, its implementation is not as simple as ours, since it's been implemented to handle many other nuances and use-cases. Nevertheless, the underlying concept remains the same.
Looking at the methods offered by Redux, You would see dispatch() and subscribe() methods which are equivalent to publish() and subscribe() methods we implemented above. You usually won't see subscribe() method getting used directly, this part is abstracted away behind connect() method offered by react-redux library. You can follow the implementation details here if that interests you.
In summary, all react components using connect() method act as subscribers. Any component using dispatch() acts as the publisher. And that explains why dispatching an action from any component causes all connected components to rerender.