Javascript Web Workers: From Basics to jQuery.Hive, Part II (Browser Implementations)


As I mentioned in Part I, the Web Worker API is available for use in Firefox 3.5+, Safari 4+ and Chrome 4+, however the implementations are inconsistant. This has been completely overlooked by every single tutorial I’ve found.


The issue revolves around the accepted argument to postMessage() (from both the main window and the worker).


The Gecko (FireFox) implementation has excellently allowed us to pass almost anything: JSON, Objects, Strings, Numbers, Booleans, Arrays, etc. through postMessage() and have them arrive on the other end as expected. In reality, postMessage() natively serializes (read: stringify) the argument before sending it to the worker and vice versa. Security risks are minimal, because all that goes through the pipes are strings, as long as the developer isn’t irresponsibly using eval() inside the worker. (Any use of eval() should be considered irresponsible).


On the other hand, the WebKit (Safari & Chrome) implementation is limited to strings. Just strings. Got it? Strings. That’s it.


In order to level the playing field, the following code would have to be used consistently throughout your application if you wanted to use something other then strings to pass messages. Take this example:

workers-3.js

//  This script is executed in the main window

/*
  we'll use a worker to filter a huge data set
  for all entries that match our filter string "foo"
*/
var worker  = new Worker('worker-javascript-file.js'),
    message = {
      fn: 'filterHugeDataSet',
      data: { /* huge data object */ },
      filter: 'foo'
    };

/*

  For this to work the same way in both Gecko and WebKit,
  we'll need to stringify the object before sending it

  Now it looks like this:

  "{"fn":"filterHugeDataSet","data":{},"filter":"foo"}"


*/worker.postMessage(JSON.stringify(message));

/*

  When the worker has filtered the data and returns it
  to our application's main window thread,
  we'll need to translate it back into an object manually

*/
worker.addEventListener('message', function (event) {

  var filtered = JSON.parse(event.data);

  console.log(filtered);

}, false);

Then in the worker…

workers-4.js

//  This script is executed in the worker

var filters = {
  filterHugeDataSet:  function (data, filter) {
    //  do some kind of filtering...
    //  this is crummy, but you get the idea
    var obj = {};

    for ( var key in data ) {
      if ( key == filter ) {
        obj[key]  = data[key];
      }
    }

    return obj;
  }
};

/*

  The worker will begin running when it receives
  a message from the main window.

  The first thing it will have to do is parse the
  message back into object.

*/
self.addEventListener('message', function (event) {

  var message   = JSON.parse(event.data),
      filtered  = {};

  /*

    `message` is now an object again. and looks how
    you expect it to:

    message = {
      fn: 'filterHugeDataSet',
      data: { foo:'bar' },
      filter: 'foo'
    };


    Use your imagination here...If we had an object
    called "filters" with a function property called
    "filterHugeDataSet" we could now call it with
    the params we passed along with the data

  */
  filtered['data'] = filters[message.fn](message.data, message.filter);


  /*

    Now we want to send it back. Once again we'll
    manually serialize the object

  */
  this.postMessage(filtered);


}, false);

Get these files from GitHub

…and run them in either FireFox, Safari or Chrome,they will all produce the same results, despite passing an object as an argument. (Requires a javascript console to see the results)

Continued in Part III

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!

Boston

201 South Street, Boston, MA 02111

New York

315 Church St, New York, NY 10013

Phone & Email

(617)379-2752 hello@bocoup.com