Messaging patterns in JavaScript

There are a great number of software development patterns that can be applied in JavaScript. Eventing, for example, provides a great mechanism to act when a known object or element emits an event you care about. But what if you want to act without knowing anything about the object or element? This is where messaging shines.

What is messaging, exactly?

There are a lot of ways to define messaging especially when discussing computer science but let's deal directly with code and define it with a comparison of Eventing.

Eventing is like a one-way message. You know an object can emit a specific event so any code can listen for that event but ONLY on that particular object. Knowledge of the object's existence is required.

const MyButton = document.getElementById("MyButton");
MyButton.addEventListener("click", (e) => {
    console.log("Clicked event!");
});

Messaging removes the aspect of requiring knowledge of what an object can emit. This allows a layer of abstraction that requires zero knowledge of where a message may come from while still handling it all the same.

msngr("click").on((p) => {
    console.log("Clicked message!");
});

In the first code snippet we're using an event listener tied directly to an HTML <button></button> element but in the second we're only listening for a click message. Both can receive and process the same data.

This doesn't mean messaging is a magic bullet that can receive all DOM events; instead this shows that the handling of a specific message, whether it comes from a DOM event or something else, can live entirely untethered from the object or element that originally emitted the message.

That was a bit contrived; what about a practical example?

Let's visit what it might look like to create an abstraction entirely using messaging. In this example let's handle updates to a user's profile for a typical web application.

// ProfileController.js
const nameInput = document.getElementById("name");
nameInput.addEventListener("change", (e) => {
    msngr("Profile", "Change").emit({ name: nameInput.value });
});

// Services.js
const wSocket = new WebSocket("ws://www.example.com/socketserver");
wSocket.onopen = (e) => {
    msngr("Profile", "Change").emit(JSON.parse(e.data));
};

// HeaderController.js
msngr("Profile", "Change").on((profile) => {
    console.log("Update profile name in header...");
    // Header update code here
});

Okay so we have a lot going on here but this is the rub: when text in a text box changes or when a web socket sends us a change, we immediately emit the change over the Profile topic and the Change category (messages here are made up of topic, category and subcategory but more on that later). This allows anything listening to pick up and handle the message which, in our example, happens to be a header within the web app which wishes to update the user's profile name whenever it's updated.

The best part of this is if all DOM + server events are handled over messages you can do browser-less + server-less unit testing on all but direct UI + server interaction allowing quick verifications of business and core application logic. This comes in handy when you want to re-use core logic but have different presentations for, say, a React Native mobile app, an Electron desktop app and a traditional web application.

Okay so what's this msngr.js thing?

Msngr is a very small JavaScript library that works in both node.js and the web browser that allows the emitting and handling of messages, asynchronously. Messages are made up of a required topic and optional category and subcategory allowing for general to very specific handling of messages. It has quite a lot of support for multiple messaging use cases including persisting a payload that can be used at a later time when the handler is registered.

Msngr.js is what I used for my examples because it's a solid library and I have a bias for it as I wrote it. But there are other libraries that can do similar things such as Postal.js.

It's also pretty easy to write a very bare bones version of a messaging / pubsub mechanism with JavaScript!

Message ALL THE THINGS

Messaging is not a silver bullet but, as you can obviously tell, I'm a huge fan of it. Give it a shot next time you're architecting a project and see if it fits your needs 👍