NCZOnline

Experimenting with web workers

In the past couple of months, there’s been some good informationfloatingaroundaboutweb workers. I have no desire to add yet another introduction to the topic into the blogosphere, but I would like to share some information about my experiences with web workers. What follows are some notes based on my playing around with the API.

Worker global scope

The interesting thing about workers is that they have their own global scope that is separate from the global scope that we all know and dread in JavaScript. Workers don’t share the browser UI thread that in-page JavaScript uses for execution, and therefore isn’t allowed access to the DOM or most of the BOM. In effect, a worker is a sandboxed environment in which JavaScript can be run completely separate from the page. This is why worker code must exist in a separate file rather than in the same location as the page code. Typical creation looks like this:

var worker = new Worker("worker.js")

The file worker.js contains all of the code to be executed within the worker. That code executes in the worker’s global scope. The global scope of a worker contains a limited set of functionality, including:

The XMLHttpRequest constructor.

A self object, which is the global object representing the worker in this scope.

I looked over this code repeatedly trying to figure out exactly what was going on. Why is there a global variable being assigned to a function? Then I discovered that self is a reference to the worker’s global scope, and decided I’d write code like this instead:

This small addition makes the example code much more readable as this pattern is very common in JavaScript. I strongly recommend that anyone writing code with web workers stick with the convention of assigning properties and calling methods directly on the self object to avoid any confusion. It’s also worth mentioning that this points to self when accessed in the global worker scope.

Worker messaging

Worker can’t affect change in a page directly, instead they rely on a messaging system to pass data back and forth. The postMessage() method is used to send data into a worker, when called on the Worker instance in the page, and to send data out of the worker when called on the worker global object. Example:

The API on both sides of the communication is exactly the same. Calling postMessage() causes a message event to be fired asynchronously on the receiver. The event handler must be assigned using the old DOM 0 style of setting onmessage equal to a function. The event object has a property called data that contains the information from the supplier.

Perhaps the most interesting thing about this messaging system is the way in which the data is transferred. You can pass any primitive data type (string, number, Boolean, null, or undefined) as well as any instance of Object or Array that isn’t part of the DOM or the BOM. The tricky part is that the values appears to be passed directly through, such as:

This code passes an object back and forth between the page and a web worker. You’ll note that the name property is accessible in both locations. This gives the appearance that the object is being passed directly through to the worker and then back. In reality, this can’t happen because the worker is in its own detached scope. What actually happens is that the value is serialized as it passes through and then deserialized on the other side. The page and the worker cannot share an object instance, only the data represented in the object. Firefox actually implements this by JSON-encoding the value and then JSON-decoding it on the other side. The end result is that a duplicate of the original data is created.

Better messaging

At first glance, the messaging system seems a bit too simple, with postMessage() just accepting a single argument to pass through. Passing a primitive value seems to be a poor way of communicating because there’s no context. I’ve now taken to passing objects all the time so I can provide better context as to the reason. Example:

By always passing an object with some contextual data, your worker knows what to do with the data it received.

Wrap-up

Workers seem like an interesting feature in browsers that may ultimately prove useful. It’s going to take a while for web developers to full grasp the idea of data-centric JavaScript that can be done in a worker versus DOM-centric JavaScript that cannot. I’m not completely convinced of worker usefulness in most web applications at this point, however. The majority of examples floating out there seem cool but aren’t things that JavaScript will or should be doing in a web application. I haven’t yet come across an instance where I’ve said, “oh man, if only web workers were widely supported, this would be easy,” and so I wonder if that day will come or if workers will be relegated to the domain of proofs-of-concept without practical applications.

Disclaimer: Any viewpoints and opinions expressed in this article are those of Nicholas C. Zakas and do not, in any way, reflect those of my employer, my colleagues, Wrox Publishing, O'Reilly Publishing, or anyone else. I speak only for myself, not for them.

Nice one Nicholas. I really wish there was more literature/examples of PRACTICAL uses of web workers. I know Mozilla had a really complex one with a video running, but would love to something that could be actually used in an app/site.

HB/Joe – I’m continuing to dig in and see if I can find a practical use for web workers. One possible thing I’ve come up with is to wrap Comet-type mechanisms in a worker, and then just expose incoming data through the postMessage() method. That would be far more compelling with a WebSocket implementation. The best use I can think of is a background syncing feature, such as Google Reader, where it may pull in more data from the server in a worker without interfering with your normal browsing. I’m sure there’s a lot of possibilities, but I too am trying to figure out the uber-practical “can’t live without it” use case.

This is just another step to help make the js environment a serious toolkit for building RIAs. Just like MVC separates your concerns when designing software, web workers can fulfill the ‘controller’ and/or ‘model’ parts of your app while keeping the ‘view’ logic on the dom-side, where we’ve been jamming all of our code for so long. It’s not a gee-whiz feature, but we should use it to build more maintainable applications.

I too have been wondering if there are any actual examples where this stuff would be useful on the web – unless we start converting all those good ‘ol Flash and Java based games on Facebook into JS driven ones

Or maybe some sort of data syncing app? Typically outside the web I use threads when loading lots of data into memory, mainly to make sure that good ‘ol progress bar…well…er…shows progress.

Anton Babushkin on August 19th, 2009 at 5:33 am

In contemporary webapplications, there doesn’t seem to be an obvious use for the Web Worker concept. However, regarding the design of the upcoming Chrome OS, I guess more and more applications will start moving towards the browser environment. When this happens with resource-heavy programs, you’ll probably be screaming for the Workers.

However, I think it still has to work its way upwards… good practices on application design with Workers doesn’t really seem to be around yet.

Joren on August 24th, 2009 at 9:22 am

Hi Nicholas,

I just want to comment that the clever messaging system works only on Firefox.
Safari and Chrome assume that the message is a string and not an object.
The spec seems to allow passing any object.

Tali Garsiel on September 24th, 2009 at 6:28 am

[...] Workers, which provides threading support for JavaScript. I’ve written about web workers in a previous post and noted that I don’t see any practical use for them right now. But in a world where [...]

I’m experimenting with Webworkers too. It is nice but I don’t get why postMessage and onMessage are only able to send data as a (JSON) string. If for example I want to do some calculating over lets say an array of 100.000 integers, the browser has to first convert this to a string which I have to convert back to an array before I can use it. Waste of valuable CPU time isn’t it?

I guess the problem here is that when you allow just any object to be posted you could also send the document or window object which is just what you don’t want to keep the GUI responsive… hmm…