Tabsub: Creating a Simple Messaging Library

vorillaz

vorillaz

Posted on April 4, 2019

Tabsub: Creating a Simple Messaging Library

There are lots of cases where developers want to post or listen to messages within an application. Posting messages across different browser contexts can vastly improve performance and user experience. Displaying queues with messages and pop notifications, synchronizing stale web views or even simultaneously logging out users from all active windows are different use cases for this method.

Demo

The Broadcast Channel API

The Broadcast Channel API was introduced as a native bus messaging interface for modern browsers.
It allows communication between different documents (in different windows, tabs, service workers, web workers, frames or iframes) of the same origin. Messages are broadcasted via a message event fired at all BroadcastChannel objects listening to the channel.

Using the BroadcastChannel API is as simple as creating a publisher and a subscriber listening to the same channel.

// subscriber
const channel = new BroadcastChannel('radio-channel');
channel.onmessage = ({
  data
}) => {
  const msg = JSON.stringify({
    data
  });
  console.log(msg);
};


// publisher
const channel = new BroadcastChannel('radio-channel');
channel.postMessage('this is a message');
channel.postMessage('This is a another one');
Enter fullscreen mode Exit fullscreen mode

You can play around with this example in JSFiddle.

The BroadcastChannel API also exposes lots of information about the publisher, the source and the origin of the message, as everything is packed into the event that’s published.

Using localStorage as an elegant fallback

Although the BroadcastChannel API is well supported, you can use the localStorage API to provide better support for older browsers. When new items are added to the storage bucket, subscribers can be notified for updates.

A simple example using the localStorage looks like this:

const channelName = 'radio-channel';

const post = msg => window.localStorage.setItem(
  channelName,
  JSON.stringify({
    date: new Date(),
    channelName,
    msg
  })
);

// Subscriber
window.addEventListener('storage', (data = {}) => {
  const {
    key = '__GIBBERISH__', newValue = '{}'
  } = data;
  if (key === channelName) {
    const value = JSON.parse(newValue);
    const {
      msg
    } = value;
    callback(`Message received: ${msg}`);
  }
});

// publisher
post('This is a message');
post({
  id: 1,
  foo: 'bar'
});
Enter fullscreen mode Exit fullscreen mode

Bringing everything together

The BroadcastChannel strategy for messaging can also be combined with the localStorage strategy, which can be used as a fallback. Checking if the BroadcastChannel is supported is as easy as:

const isBroadcastSupported = window && window.BroadcastChannel;
Enter fullscreen mode Exit fullscreen mode

Introducing Tabsub

In order to reduce friction and create a solid solution to achieve internal communication, I have combined the examples above into tiny (~500 bytes) library called tabsub.
The API is lean and minimal and the library can be used right away.

import radio from 'tabsub';

const channel = radio('channel-name');

// Post to channel
channel.post('this is a message');

// Subscribe
channel.on(msg => {
  console.log(`Received: ${msg}`);
});

// Stop listening for a while
channel.stop();

// Resume listening for messages
channel.start();

// Close the channel
channel.close();
Enter fullscreen mode Exit fullscreen mode

Further resources

You can also find this post on vorillaz.com

💖 💪 🙅 🚩
vorillaz
vorillaz

Posted on April 4, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related