How JavaScript works: the publisher-subscriber pattern

Lawrence Eagles
SessionStack Blog
Published in
8 min readOct 7, 2021

This is post # 47 of the series, dedicated to exploring JavaScript and its building components. In the process of identifying and describing the core elements, we also share some rules of thumb we use when building SessionStack, a JavaScript tool for developers to identify, visualize, and reproduce web app bugs through pixel-perfect session replay.

Introduction

The ability to identify recurring patterns or themes in our code is an essential requirement for its optimization. And this is where the understanding of design patterns is invaluable.

Design patterns are reusable solutions to common software design problems.

It is important to note that design patterns are not complete solutions themselves but they provide us with a solution scheme. We can think of them as templates for solving software design problems.

Design patterns give us proven approaches to common software development problems and since they are language agnostic, they can be used and reused by anyone with any language.

Although they reflect the experience of the developers that defined them, they are extensible. Thus other developers can adapt them to suit their needs.

The concept of design pattern has long been in programming but in a less formal form. However, in 1995, a group popularly known as the gang of four or GoF published their book: Design Patterns: Elements of Reusable Object-Oriented Software.

This GoF publication fostered the popularity of design patterns in software development and it is often regarded as the most iconic design pattern publication to date.

In their book, they provided twenty-three object-oriented patterns that are in use to date as well as a number of software development techniques and pitfalls.

You can learn more about these patterns and why they are needed in a previous article that talks more generally about design patterns.

In this article, Our focus is on the publisher-subscriber pattern. Let’s get started with an overview in the next section.

The observer pattern

The observer pattern is a design pattern in which you define a one-to-many relationship from one object known as the subject to many other objects known as the observers.

These observers can be functions that watch the subject and wait for a signal or notification from the subject before they run.

In the observer pattern when an object is modified, it notifies its dependent objects that changes have occurred to its state. An example is the model-view-controller architecture. When the view updates the model changes.

Event handlers are another example of the observer pattern. Event handlers are functions that listen for a specific event and they only run when that event is fired.

The observer pattern consists of a subject that keeps track of all the observers that are currently subscribed to it. The subject also contains methods that facilitate adding, removing, and notifying these dependent objects.

Below is a simple implementation of the observer pattern:

In the code above, the Subject constructor maintains a list of observers. And we added the relevant methods to the prototype of the Subject. This enables us to share these methods with every instance of the Subject.

If you do not understand how we can share methods using an object’s prototype, I suggest you read a previous article that covers prototypal inheritance in JavaScript.

In the code above, the subscribe method adds observers to the observers array while the unsubscribe method removes observers from the observers array. The notify method would notify the specified subscribed observer while the notifyAll method notifies all the subscribed observers.

With the observer pattern, each observer is required to subscribe to the object firing the event — — the subject before it can get a notification. However, the pub/sub pattern does not require this. The pub/sub pattern uses a middleware that sits between the objects firing the event. In this case, the publishers and the subscribed objects or the subscribers. Let’s take a deep dive into the pub/sub design pattern in the next section.

Deep dive into the publisher-subscriber pattern

The pub/sub pattern involves a middleware that is also referred to as the pub/sub broker. The pub/sub broker handles interactions between the publishers and the subscribers. Publishers publish contents or publications to the pub/sub broker and it handles the delivery of these contents to the appropriate subscriber.

The pub/sub broker also enables loose decoupling of publishers and subscribers and it supports many to many relationships between the publishers and the subscribers.

So unlike the observer pattern, the pub/sub pattern allows multiple publishers and multiple subscribers.

In the pub/sub pattern a publisher publishes contents to a topic and interested subscribers access these contents by sending subscriptions to the pub/sub broker to subscribe to that topic. Also, unlike the observer pattern, both the publishers and the subscribers do not need to be aware of each other.

Consider the image below:

pubsub broker

There are many implementations of the pub/sub patterns, some examples are IBM Websphere MQ, RabbitMQ, and RocketMQ , Apache Kafka, and Google Cloud Pub/Sub, and Pushy.

In this article, we will implement a basic system using JavaScript.

The JavaScript language is well suited for the pub/sub pattern because at its core most ECMAScript implementations are event-driven.

Our pub/sub implementation consists of a pub/sub class that contains an events array that is used to maintain the list of all published events.

Also, the pub/sub class has a subscription method that handles all interactions between the publishers and subscribers.

Let’s see the implementation below:

In our small example above, the subscription method returns an object containing a subscribe method used to handle subscriptions and an unsubscribe method that handles unsubscriptions.

Lastly, our pub/sub class contains a publish method that takes a variable number of arguments and invokes all the functions that are subscribed to the specified event with these arguments, using apply. You can learn more about how apply works by reading a previous article in this series.

Advantages of the publish/subscribe pattern

The loose decoupling of the Pub/sub pattern makes it suitable for many software engineering problems. It is highly scalable and well-fitted for distributed architectures such as microservices.

Pub/sub shines when building event notifications, distributed caching, distributed logging, and multi-data source systems.

Some examples of real-life applications that use the pub/sub pattern are Redis, Split, Twillo, and Gutenberg created by Netflix.

Pub/sub pattern vs Observer pattern vs data binding

We have learned above that the main difference between the pub/sub and the observer pattern is that the pub/sub pattern offers complete decoupling of the subscribers from the publishers. In the observer pattern, however, the observers or subscribers must be aware of the subject — also known as the observables.

Also, the observer pattern does not have a broker and the observables emit the notifications themselves.

Data binding is a general term. In a nutshell, it simply means “the value of property X on object Foo is semantically bound to the value of property Y on object Bar. There are no assumptions on how Foo knows or is fed changes on object Bar.

Data bind can be implemented using either the pub/sub or observer pattern. And the data is the Publisher/Observable.

Conclusion

The pub/sub and observer patterns are design patterns that every developer should be familiar with.

They encourage us to think hard about the relationships between the different components of our application. And they help us to organize our application components into smaller loosely coupled units that are easier to maintain and reuse.

SessionStack utilizes pub/sub services in order to process all of the ingested behavioral data from the browser in real-time. As the data is being ingested, SessionStack allows you to watch user sessions as videos, allowing you to see exactly what happened during their journey.
Combining this visual information with all of the tech data from the browser such as errors, stack traces, network issues, debug data, etc. you can easily understand problematic areas in your product and efficiently resolve them.

There is a free trial if you’d like to give SessionStack a try.

SessionStack replaying a session

If you missed the previous chapters of the series, you can find them here:

--

--