HTML5 Web Workers are the web’s answer to multi-threading. They’ve been around for a while now, so are pretty safe to rely on. A Worker is typically initialised by instantiating Worker with the URL of your ‘worker script’:

var myWorker = new Worker('path/to/my/worker.js');

And then you’d interface with the worker via asynchronous message-passing:

// == Within parent page ==
myWorker.postMessage('Did you receive this?');
myWorker.onmessage = function() {
  if (e.data === 'I sure did, friend.') {
    myWorker.postMessage('Cool, I am gonna send you some data, k?');
  }
  // etc.
}
 
// == Within worker ==
self.onmessage = function(e) {
  if (e.data === 'Did you receive this?') {
    self.postMessage('I sure did, friend.');
  }
  // etc.
};

The message-passing code I tend to see in the wild is typically un-abstracted and chaotic. I felt there was some things worth improving in this area so I created operative.

Operative allows you to define worker code inline without having to create specific files and load them separately at runtime. With operative it’s as simple as:

var calculator = operative({
 
  // Any methods defined here are executed within a worker!
 
  doComplexThing: function(a, b, c) {
    // do stuff.
    return result;
  }
 
});
 
// Call method, passing a callback as the last arg:
calculator.doComplexThing(1, 2, 3, function(result) {
  result; // => value returned from within worker
});

Additional benefits include:

  • Debuggability (Blob + Chromium Web Inspector = Pure Bliss)
  • Console debug methods for free (log, warn, debug etc.)
  • Degradability / Progressive Enhancement (It works with no/partial worker support)

Operative’s Degradability

Operative degrades in this order:

(higher is better/cooler)

  • Full Worker via Blob & Structured-Cloning (Ch13+, FF8+, IE11+, Op11.5+, Sf5.1+)
  • Full Worker via Eval & Structured-Cloning (IE10)
  • Full Worker via Blob & JSON marshalling (???)
  • Full Worker via Eval & JSON marshalling (Sf4)
  • No Worker: Regular JS called inline (older browsers = slow)

Operative will degrade in environments with no Worker support. In such a case the code would execute as regular in-place JavaScript. The calls will still be asynchronous though, not immediate.

Leaky but worth it?

The abstraction that operative offers is somewhat leaky. I see this is an acceptable leakiness. I would hope that anyone using operative understands what’s really happening behind the scenes.

One particular leak is the lack of the native worker APIs, such as importScripts, in the fully-degraded state which runs JavaScript in-place. If you’re just aiming for newer browsers then you can use importScripts to your heart’s content though.

Other leaks are the subtle differences you could encounter between structured-cloning and the degraded JSON-transfer approach. E.g. one understands /regexes/ and the other doesn’t. Operative uses JSON stringify/parse where structured-cloning is unavailable.

Basically, short of creating a new DSL with severely limited possibilities, I very much doubt it’s even possible to create a non-leaky worker abstraction. And I think that’s ok. Operative isn’t perfection. It’s just a tad better and a tad more seamless than the conventional way of utilising workers… And that is sufficient benefit for me to feel okay with using it.

Other options

I wasn’t even going to make operative degradable until Calvin Metcalf mentioned his similar-in-principle communistjs library which does degrade. Seeing it as a possibility I decided to implement JSON-transfer fallbacks and an eval option so that IE10 (the browser of rubbish worker/blob support) could be used.

I suggest checking out both communistjs library and operative if you’re looking to abstract away Worker awkwardness.

Also, watch out, operative is new and potentially buggy. It’s also a bit of an experiment. I’m not even sure this kind of thing is worth abstracting.

Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!