Build a Pub-Sub in JavaScript | Skilled.dev
Interview Question

Pub-Sub and Event-Driven Programming

Loading...

Event-driven programming is incredibly important in software engineering, and it's the foundation of JavaScript (both in the browser and Node.js). Events are emitted on interaction either by a user or triggered from within the program, and there are listeners that execute a callback when they recognize a specific event.

You can see instances of event-driven programming in:

  • Browser events (onclick, onload, onkeyup, etc.)
  • Node.js server events
  • Sockets
  • Messaging
  • Redux and other state management libraries
  • RxJS and other event libraries
  • GraphQL subscriptions

There are different names for the various event design patterns, but the core concept is the same. In this lesson, we will build a Pub-Sub, but the intuition will be transferable. Other similar concepts are:

  • Observer pattern
  • Event bus
  • Message queue
  • Event-driven architecture

There are subtle differences between the different pattern implementations, but the important learning outcomes are all the same. In this question, you will build a Pub-Sub (publish-subscribe pattern) where publishers emit actions and subscribers listen for a specific action and execute a callback accordingly.

We will implement the Pub-Sub as a class with subscribe, publish, unsubscribe methods. They will communicate through an eventName channel which is just a string. It should be built so that many subscribers can listen for the same event.

 

Then to use it, we do the following:

 

Pub-Sub Implementation

Our Pub-Sub implementation will give us the ability to subscribe to an event, publish data to an event, and a place to store the subscribed callbacks. In the constructor, we initialized a subscriptions object whose key-value pairs will be the event channel which maps to an array of event callbacks.

subscribe method

The first thing we should implement is the subscribe method. The function takes an eventName which is a string, and a callback which is a function that executes with the data.

 

First we check if an array of callbacks in subscribers already exists for the eventName channel, and if it doesn't exist, we initialize it now. Then we push the callback to this array which is a function that takes a single argument.

publish method

When we want to trigger an event, we publish under the eventName and pass it the data for that event. Our callbacks should take at most one argument, but it could take no arguments if it does not use the data.

In our case, the data can be anything, but you could enforce it to be something specific either in your Pub-Sub implementation or by the callback for a specific eventName.

When we publish an event, we first check that the eventName has been initialized and return early if it has not. If it does exist, we iterate through all the subscribers for that eventName and execute their callbacks.

 

unsubscribe method

We want to allow our code to clean up a subscription and remove its listener. We pass an eventName and the same callback from the subscribe method to remove it. Then the callback is filtered out of our subscribers for that channel.

Functions are stored in memory as in JavaScript. You must pass the exact same function you used originally which means you'll need to store it in a variable. See how this is done in the solution REPL.

 

Full Implementation

Interact with this solution in a REPL.

 

Wrap Up

The publish-subscribe pattern is actually simple to implement, but the usage is very powerful. This is a common JavaScript interview question because it shows an understanding of an essential pattern and knowledge of fundamental JS concepts.

One challenge developers have with a Pub-Sub is the terminology because it makes it sound much more challenging than it actually is. When we say a subscriber is "listening", all this means is that we have a hash table (object) that has an array of events for a given key. A subscriber adds its callback to this array, and the publisher just executes all the callback under that key when it "emits" the matching event.

If you have used Redux, what's happening underneath the hood is almost the exact same. The only difference is that we are storing state inside our Pub-Sub, and the eventName (action.type now) is passing the action to every reducer, and they decide if they should update a piece of state based on the action. Below is a simple implementation with phrases changed to Redux terminology:

 

For larger system Pub-Subs, message queues, and observers, it requires more robust storage and communication channels, but the fundamentals are still the exact same. Maintain channel names and a list of functions to execute.

Loading...

Table of Contents