• Stars
    star
    1,845
  • Rank 25,153 (Top 0.5 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 6 years ago
  • Updated 22 days ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

📡 BroadcastChannel to send data between different browser-tabs or nodejs-processes 📡 + LeaderElection over the channels https://pubkey.github.io/broadcast-channel/

BroadcastChannel

A BroadcastChannel to send data across multiple browser-tabs or nodejs-processes
+ LeaderElection over the channels

follow on Twitter

demo.gif


A BroadcastChannel that allows you to send data between different browser-tabs or nodejs-processes.

  • It works completely client-side and offline,
  • Tested on old browsers, new browsers, WebWorkers, Iframes and NodeJs.

This behaves similar to the BroadcastChannel-API which is currently only featured in some browsers.

Sponsored by

JavaScript Database

The JavaScript Database

Using the BroadcastChannel

npm install --save broadcast-channel

Create a channel in one tab/process and send a message

import { BroadcastChannel } from 'broadcast-channel';
const channel = new BroadcastChannel('foobar');
channel.postMessage('I am not alone');

Create a channel with the same name in another tab/process and recieve messages

import { BroadcastChannel } from 'broadcast-channel';
const channel = new BroadcastChannel('foobar');
channel.onmessage = msg => console.dir(msg);
// > 'I am not alone'

Add and remove multiple eventlisteners

import { BroadcastChannel } from 'broadcast-channel';
const channel = new BroadcastChannel('foobar');

const handler = msg => console.log(msg);
channel.addEventListener('message', handler);

// remove it
channel.removeEventListener('message', handler);

Close the channel if you do not need it anymore

Returns a Promise which is resolved when everything is processed.

await channel.close();

Set options when creating a channel (optional)

const options = {
    type: 'localstorage', // (optional) enforce a type, oneOf['native', 'idb', 'localstorage', 'node']
    webWorkerSupport: true; // (optional) set this to false if you know that your channel will never be used in a WebWorker (increases performance)
};
const channel = new BroadcastChannel('foobar', options);

Create a typed channel in typescript

import { BroadcastChannel } from 'broadcast-channel';
declare type Message = {
  foo: string;
};
const channel: BroadcastChannel<Message> = new BroadcastChannel('foobar');
channel.postMessage({
  foo: 'bar'
});

Enforce options globally

When you use this module in a test-suite, it is recommended to enforce the fast simulate method on all channels so your tests run faster. You can do this with enforceOptions(). If you set this, all channels have the enforced options, no mather what options are given in the constructor.

import { enforceOptions } from 'broadcast-channel';

// enforce this config for all channels
enforceOptions({
  type: 'simulate'
});

// reset the enforcement
enforceOptions(null);

Clear tmp-folder

When used in NodeJs, the BroadcastChannel will communicate with other processes over filesystem sockets. When you create a huge amount of channels, like you would do when running unit tests, you might get problems because there are too many folders in the tmp-directory. Calling BroadcastChannel.clearNodeFolder() will clear the tmp-folder. It is recommended to run this at the beginning of your test-suite.

import { clearNodeFolder } from 'broadcast-channel';
// jest
beforeAll(async () => {
  const hasRun = await clearNodeFolder();
  console.log(hasRun); // > true on NodeJs, false on Browsers
})
import { clearNodeFolder } from 'broadcast-channel';
// mocha
before(async () => {
  const hasRun = await clearNodeFolder();
  console.log(hasRun); // > true on NodeJs, false on Browsers
})

Handling IndexedDB onclose events

IndexedDB databases can close unexpectedly for various reasons. This could happen, for example, if the underlying storage is removed or if a user clears the database in the browser's history preferences. Most often we have seen this happen in Mobile Safari. By default, we let the connection close and stop polling for changes. If you would like to continue listening you should close BroadcastChannel and create a new one.

Example of how you might do this:

import { BroadcastChannel } from 'broadcast-channel';

let channel;

const createChannel = () => {
  channel = new BroadcastChannel(CHANNEL_NAME, {
    idb: {
      onclose: () => {
        // the onclose event is just the IndexedDB closing.
        // you should also close the channel before creating
        // a new one.
        channel.close();
        createChannel();
      },
    },
  });

  channel.onmessage = message => {
    // handle message
  };
};

Methods

Depending in which environment this is used, a proper method is automatically selected to ensure it always works.

Method Used in Description
Native Modern Browsers If the browser supports the BroadcastChannel-API, this method will be used because it is the fastest
IndexedDB Browsers with WebWorkers If there is no native BroadcastChannel support, the IndexedDB method is used because it supports messaging between browser-tabs, iframes and WebWorkers
LocalStorage Older Browsers In older browsers that do not support IndexedDb, a localstorage-method is used
Sockets NodeJs In NodeJs the communication is handled by sockets that send each other messages
Simulate none per default This method simulates the behavior of the other methods but only runs in the current process without sharing data between processes. Use this method in your test-suite because it is much faster.

Using the LeaderElection

This module also comes with a leader-election which can be used to elect a leader between different BroadcastChannels. For example if you have a stable connection from the frontend to your server, you can use the LeaderElection to save server-side performance by only connecting once, even if the user has opened your website in multiple tabs.

In the background it will use the Web Locks API if possible and fall back to a message-based election algorithm if WebLocks is not available.

In this example the leader is marked with the crown ♛: leader-election.gif

Create a channel and an elector:

import {
  BroadcastChannel,
  createLeaderElection
} from 'broadcast-channel';
const channel = new BroadcastChannel('foobar');
const elector = createLeaderElection(channel);

Wait until the elector becomes leader:

import { createLeaderElection } from 'broadcast-channel';
const elector = createLeaderElection(channel);
elector.awaitLeadership().then(()=> {
  console.log('this tab is now leader');
});

Check if there is a leader at this point in time. hasLeader() returns true when there is a leader. It returns false, if the leader is dead. Then it returns true again when a new leader is elected.

const elector = createLeaderElection(channel);
const hasLeader = await elector.hasLeader();
console.log('leader exists: ' + hasLeader);

If more than one tab is becoming leader adjust LeaderElectionOptions configuration.

import { createLeaderElection } from 'broadcast-channel';
const elector = createLeaderElection(channel, {
  fallbackInterval: 2000, // optional configuration for how often will renegotiation for leader occur
  responseTime: 1000, // optional configuration for how long will instances have to respond
});
elector.awaitLeadership().then(()=> {
  console.log('this tab is now leader');
})

Let the leader die. (automatically happens if a tab is closed or the process exits).

const elector = createLeaderElection(channel);
await elector.die();

Handle duplicate leaders

Duplicate leadership can happen on rare occurences like when the CPU is on 100% for longer time, or the browser has throttled the javascript timers.

const elector = createLeaderElection(channel);
elector.onduplicate = () => {
  alert('have duplicate leaders!');
}

What this is

This module is optimised for:

  • low latency: When you postMessage to a channel, it will be delivered to other channels as soon as possible,
  • lossless: When you send a message, it should be impossible that the message is lost before other channels recieved it,
  • low idle workload: During the time when no messages are send, there should be a low processor footprint.

What this is not

  • This is not a polyfill. Do not set this module to window.BroadcastChannel. This implementation behaves similiar to the BroadcastChannel-Standard with these limitations:
    • You can only send data that can be JSON.stringify-ed,
    • While the offical API emits onmessage-events, this module directly emitts the data which was posted.
  • This is not a replacement for a message queue. If you use this in NodeJs and want send more than 50 messages per second, you should use proper IPC-Tooling.

Browser Support

I have tested this in all browsers that I could find. For ie8 and ie9 you must transpile the code before you can use this. If you want to know if this works with your browser, open the demo page.

Thanks

Thanks to Hemanth.HM for the module name.

More Repositories

1

rxdb

A fast, local first, reactive Database for JavaScript Applications https://rxdb.info/
TypeScript
21,520
star
2

eth-crypto

Cryptographic javascript-functions for ethereum and tutorials to use them with web3js and solidity
JavaScript
876
star
3

client-side-databases

An implementation of the exact same app in Firestore, AWS Datastore, PouchDB, RxDB and WatermelonDB
TypeScript
862
star
4

event-reduce

An algorithm to optimize database queries that run multiple times https://pubkey.github.io/event-reduce/
TypeScript
720
star
5

jsonschema-key-compression

Compress json-data based on its json-schema while still having valid json
TypeScript
96
star
6

unload

Run a piece of code when the javascript process stops. Works in all environments (browsers, nodejs..)
JavaScript
55
star
7

solidity-cli

Compile solidity-code faster, easier and more reliable
TypeScript
50
star
8

rxdb-quickstart

Local-First peer-to-peer replicated todo list with RxDB and WebRTC
TypeScript
41
star
9

fashion-segmentation

A tensorflow model for segmentation of fashion items out of multiple product images
Python
38
star
10

vscode-in-docker

Run VSCode inside of a Docker Container
Dockerfile
35
star
11

binary-decision-diagram

A library to create, minimize and optimize binary decision diagrams https://github.com/pubkey/binary-decision-diagram
TypeScript
31
star
12

async-test-util

Utility functions that are useful in async/await tests 👍
JavaScript
27
star
13

custom-idle-queue

Optimize the performance of important tasks by delaying background-tasks
JavaScript
19
star
14

atomjsIDE

The atom.io-IDE with all necessary plugins for fast javascript-programming. Build as a docker-container in linux.
Shell
13
star
15

array-push-at-sort-position

Push items to an array at their correct sort-position
JavaScript
8
star
16

javascript-vector-database

Local-First Vector Database with RxDB and transformers.js
TypeScript
8
star
17

oblivious-set

Like a JavaScript Set() but with a TTL for entries
TypeScript
7
star
18

sticky-load-balancer

NPM-Module for Nodejs to create a loadbalancer with a sticky-strategie.
JavaScript
4
star
19

secure-json-logic

Use logic-objects from uncertain sources and run them locally without breaking the own system
JavaScript
4
star
20

localstorage-indexeddb-cookies-opfs-sqlite-wasm

Localstorage vs. IndexedDB vs. Cookies vs. OPFS vs. Wasm-SQLite
TypeScript
3
star
21

indexeddb-performance-tests

Performance tests for IndexedDB use cases
TypeScript
3
star
22

rxdb-server

RxDB Server - https://rxdb.info/rx-server.html
TypeScript
3
star
23

dhbw-thesis-roter-grubert

Latex Vorlage fĂźr Bachelor- & Masterarbeiten an der DHBW nach dem Format wie im Buch "Roter Grubert" beschrieben
HTML
2
star