Javascript Web Workers: Opera 10.6 Beta Supports SharedWorkers


While poking around the Web Worker API in the latest Opera Beta Release, I discovered that they had also implemented support for Shared Web Workers. If you’re not familiar with Shared Web Workers, have a look here. The basic premise is that a Shared Worker can have multiple connections made to one Worker.


Paraphrased from the specification for clarity:


[Instead of a single message processing function, workers can attach multiple event listeners, each one performing a quick check to see if it is relevant for the message. If multiple authors wanted to collaborate using a single port to communicate with a worker, it would allow for independent code instead of changes having to all be made to a single event handling function.]

So how about some code? In order to run the demo I’ve prepared, you’ll need Opera 10.6 Beta 1. Then hop over to Github and grab the Gist.


The comments will guide you through each step of the demo’s functionality.


Basic HTML page for running the test:

sharedworker.multi-connect.html

<!DOCTYPE HTML>
<html>
  <head>
    <title>SharedWorker: Multiple Connections</title>
    <!-- Include Firebug Lite Because Dragonfly is terrible -->
    <script src="https://getfirebug.com/releases/lite/beta/firebug.jgz">
    {
      startOpened: true
    }
    </script>
    <script src="sharedworker.multi-connect.renderer.js"></script>
  </head>
  <body>
    <pre id="shared-worker-log"></pre>
    <iframe style="width:100%" src="sharedworker.multi-connect-inner.html"></iframe>

    <pre id="shared-worker-connection-log"></pre>
  </body>
</html>

The HTML page called in the iframe:

sharedworker.multi-connect-inner.html

<!DOCTYPE HTML>
<html>
  <head>
    <script src="sharedworker.multi-connect.renderer.js"></script>
  </head>
  <body>
    <pre id="shared-worker-log"></pre>
  </body>
</html>

The Renderer (that’s your browser window)

sharedworker.multi-connect.renderer.js

document.addEventListener('DOMContentLoaded', function () {

  var Share  = {
    worker: (function () {
              //  CREATE SHARED WORKER AND RETURN IT
              return new SharedWorker('sharedworker.multi-connect.worker.js');
            })(),
    logTo:    document.getElementById('shared-worker-log'),
    reportTo: document.getElementById('shared-worker-connection-log')
  };


  //  REFLECT Share OBJECT
  console.log(Share);

  //  LISTEN ON THE SHAREDWORKER'S PORT FOR NEW MESSAGES
  Share.worker.port.addEventListener('message', function(event) {

    //  INITIAL CONNECTION
    if ( event.data.connected ) {
      var workerLog = 'ConnectionId #' + event.data.connectionId +
                                   ' ' + event.data.pathName +
                      ' - Connected: ' + event.data.connected ;

      //  APPEND TO LOG FIELD
      Share.logTo.textContent  += "n" + workerLog;

      return;
    }

    //  REPORTING CONNECTIONS TO SHARED WORKER
    if ( event.data.connections ) {
      var connectionPaths = event.data.connections;

      console.log('Total Connections: ' + connectionPaths.length);

      for ( var id in connectionPaths ) {

        if ( id !== 'length' )  {

          var connectionLog = '#' + id + ' ' + connectionPaths[id];

          //  WRITE TO CONSOLE
          console.log( connectionLog  );

          //  APPEND TO REPORT FIELD
          Share.reportTo.textContent  += "n" + connectionLog;
        }
      }
      return;
    }
  }, false);

  //  START THE CONNECTION TO SHAREDWORKER
  //  REQUIRED WHEN USING "addEventListener()"
  Share.worker.port.start();
  //  FIRE CONNECTING MESSAGE TO SHAREDWORKER
  Share.worker.port.postMessage({
    'pathName': location.pathname,
    'connected' : false
  });
}, false);

The SharedWorker

sharedworker.multi-connect.worker.js


var Connection = { count: 0, isConnected: false, paths: { length: 0 } }; /* self.addEventListener('connect', callback, false); does not work */onconnect = function(event) { // ASSIGN PORT TO VAR POINTER var port = event.ports[0]; // INCREMENT CONNECTION COUNT Connection.count++; // REPLY TO RENDERER, CONFIRMING CONNECTION port.postMessage({ 'connectionId' : Connection.count }); /* port.addEventListener('message', callback, false); does not work */ // SET UP LISTENER ON PORT port.onmessage = function(event) { // STORE A REF TO THE CONNECTING RENDERER PAGE Connection.paths[Connection.count] = event.data.pathName; Connection.paths.length++; // UPDATE CONNECTION TO TRUE event.data.connected = true; // UPDATE WITH THIS CONNECTION ID event.data.connectionId = Connection.count; // REPLY TO RENDERER port.postMessage(event.data); } // REPORT CONNECTIONS setTimeout(function () { port.postMessage({ 'connections' : Connection.paths }); }, 1000); }

This demonstrates how we can connect two different pages to the same SharedWorker process, and track our connections to them from one persistent object variable. Very exciting!

EditIn the time since this was originally published, Chrome, Safari & Opera now support complex JSON messages.

Comments

Contact Us

We'd love to hear from you. Get in touch!

Phone

+1 617-379-2752

Mail

P.O. Box 961436
Boston, MA 02196