Using Web Workers Today
I did a little reading on Web Workers a few months ago, and I recently used them in a little test I was asked to perform, so I thought I would share what I've discovered since I don't see much in Google search results about how to use Web Workers in a cross browser fashion with graceful degradation.
Web Workers
For those of you that do not know what Web Workers are I suggest you read the Web Workers Draft Recommendation, but in brief they provide web developers with the ability to create thread-like operations.
This is obviously useful for long operations. The standard way of dealing with laborious tasks like these in the past was to split up the work with multiple setTimeout() calls.
setTimeout
Before Web Workers, setTimeout() had to be used to split up long operations in to multiple small operations, because you only had a single thread, so this had to be done in order to avoid having an unresponsive UI or triggering a unresponsive script warning.
Current Browser Support For Web Workers
Currently the latest versions of Firefox, Chrome, and Safari all support Web Workers; IE and Opera do not support Web Workers.
So how do you detect if a browser supports Web Workers programmatically? this is how:
// Web Workers are supported
else
// Web Workers are not supported
Minor Note For Safari
While Safari supports Web Workers it requires that strings be used when passing messages, which is something that Firefox and Chrome do not require. Firefox and Chrome both stringify anything not already a string that is crossing the bridge to or from a Web Worker, but Safari does not do this, so you must. This is a simple thing to do, but one that you must remember, always use the JSON object to JSON.stringify when sending a message and JSON.parse when receiving a message (if web workers are supported, then so will the JSON object be).
Supporting Browsers That Do Not Support Web Workers
The biggest hurdle for most to implement Web Workers today is the fact that not all browsers support them, but the fact is that it is simple to degrade gracefully, thus allowing those users that are using browsers that support web workers to start reaping the rewards, and allowing you to improve your site and be prepared for when IE and Opera finally come around.
So what is the best way to handle the browsers that do not support Web Workers? well I see two options:
- Maintain a secondary script to be used when Web Workers cannot be, and add it to the page by creating a script element and appending it to the document.body.
- Make the Web Worker friendly to being used in a window scope instead of a Web Worker's scope so that it can be simply added to page like I describe in option 1.
Option 2 will be the better approach in most cases I suspect, because it means that two separate files with two slightly different methods of achieving the same task do not need to be maintained. In order to implement option 2 two there are some things to consider:
- You cannot use functions like postMessage, onmessage, or importScripts while in the window scope.
- You will need to split the work up with setTimeout (which is available in the Web Worker scope as well) to avoid an unresponsive UI and unresponsive script warnings.
- You should assume that your Web Worker will be used in the window scope, so do not pollute your global scope.
There are a couple of ways to determine if you are not in a Web Worker's scope, as to avoid using the postMessage, onmessage, and importScripts functions (which are available to Web Workers) when your script is run in the window scope (postMessage has another purpose in the window scope). The first method is this:
// web worker scope
}
else {
// not web worker scope, assume window scope
}
The other method is to check if you are in the window scope:
// window scope
}
else {
// not window scope, assume web worker scope
}
Or, combining the two methods above:
// web worker scope
}
else {
// another scope
}
These methods will allow us to avoid using postMessage, onmessage, or other methods we use while in the scope of a Web Worker.
To avoid polluting the global space normal tactics apply, I would simply suggest wrapping all of the code in an anonymous automatically executed function, like so:
// this is an anonymous automatically executed function
})()
Putting these methods together, we would get web workers like so:
var winScope = true;
if(typeof window == "undefined" && typeof importScripts == "function") {
// web worker scope
winScope = false;
}
if(!winScope) {
onmessage = function() {
postMessage("blah");
}
}
else {
alert("blah");
}
})()
Except that real Web Workers would do interesting things, and use setTimeout.

@erikvold