Shared Worker

A SharedWorker is a special type of workers shared between different tabs, iframes, etc. under the same origin. If you do not know what origin is, that is OK for now---please take my Web Security class.

Creating Shared Worker

We use the following to create or access a shared worker:

let myWorker = new SharedWorker('worker.js');

The main thread connects to a shared worker via SharedWorker.port property. You can manually start the connection via the following:

myWorker.port.start();

This is especially needed if the onmessage event is attached using addEventListener.

Communicating with Shared Worker

Then, you can use postMessage and onmessage like a normal web worker.

myWorker.port.postMessage("SharedWorker");
myWorker.port.onmessage = function(e) {
    console.log(e.data);
};

The code on the SharedWorker side looks quite different, which need to register an onconnect event for communicating with an instance.

onconnect = function(e) {
  var [port] = e.ports;

  port.addEventListener('message', function(e) {
    port.postMessage(e.data);
  });

  port.start(); // Required when using addEventListener. Otherwise called implicitly by onmessage setter.
}

A Simple Example Using SharedWorker

A shared worker provides a shared scope among all scripts under the same origin. Let us design a simple application with a sender and a receiver. These two pages are located on two tabs of a browser, which do not share any handler (i.e., we cannot directly use postMessage for communication). Note that theoretically, we can still use localStorage, but we just illustrate an example to demonstrate the advantage of SharedWorker.

Let us start from sender.js:

let myWorker = new SharedWorker('worker.js');

myWorker.port.start();

myWorker.port.postMessage("Let us learn SharedWorker together!")

The above JavaScript code just sends a constant string to the shared worker.

We then describe receiver.js:

let myWorker = new SharedWorker('worker.js');

myWorker.port.onmessage = function(e) {
  document.querySelector('#app').innerText = `Message from Worker: ${e.data}`
}

The above JavaScript code receive data from the shared worker and displays it on the HTML page.

Note that we encourage you to use innerText wherever it is possible.

Now we describe the important part, worker.js:

let msg = {};
let tabInstances = [];

onconnect = function(e) {
  var port = e.ports[0];

  port.addEventListener('message', function(e) {
    msg.data = e.data;
    msg.port = port;
  });
  port.start(); 
  tabInstances.push(port);

  tabInstances.forEach(instance => {
      if (instance != msg.port)
        instance.postMessage(msg.data);
  });
}

There are two take-aways:

  • msg is shared under the worker scope when two tabs are connecting with the same worker.
  • Different tabs are communicating with the sharing worker using different ports.

Debugging Shared Worker

You may notice that the SharedWorker script is not shown when you open the console. If you want to debug a SharedWorker, go to the following page and then click inspect.

chrome://inspect/#workers