Using HTML5 Web Workers to have background computational power
When performing advanced load-heavy operations in a web browser, both the web page it is run in as well as the web browser UI becomes unresponsive till it’s finished. However, there’s a way to address that with HTML5 Web Workers.
What they do
Basically, Web Workers offers you a possibility to load a JavaScript file dynamically and then have it process code in a background process, not affecting the user interface and its response level. You can continue to do whatever you want, selecting thing, clicking etc, while all Web Worker computation is in the background.
Working with it
First, you create a Worker
object. Then you can send text data to it with a postMessage
method, it can send information back with its own postMessage
and both of them have onmessage
event handlers to receive data. For instance, here you have a simple example where you send in some data to a Web Worker, have it do 1 000 000 iterations in a loop, and continually post the current value back:
In the web page
// Creates a Web Worker var worker = new Worker("worker.js"); // Posts a message to the Web Worker worker.postMessage(0); // Triggered by postMessage in the Web Worker worker.onmessage = function (evt) { // evt.data is the values from the Web Worker alert(evt.data); }; // If the Web Worker throws an error worker.onerror = function (evt) { alert(evt.data); };
In the Web Worker JavaScript file
// Triggered by postMessage in the page onmessage = function (evt) { // evt.data will be 0 here from the above postMessage for (var i=evt.data, il=1000001; i<il; i++) { // Continually sends data back postMessage(i); }; };
In addition to the postMessage
method and the onmessage
event used above, there is a handy terminate
method on the Worker
object, to instantly stop all of its work.
This demo and complete code is available in the HTML5 Web Worker Demo page, which is part of my HTML5 – Information and samples for HTML5 and related APIs playground.
Web Browser support
So far, Web Workers are supported in Firefox 3.5+ and Safari 4, and soon upcoming in Google Chrome.
Issues
All of this is quite nice for heavy operations. However, there are to main issues related to the terminate
method:
Firefox and execution of terminate
As you can see in the HTML5 Web Worker Demo, in Firefox, if you click on the “Stop Worker” button, it isn’t actually called till the previous ongoing operation is finished. To me, this practically renders it useless since the whole idea of the terminate
method is supposed to be instant, and which seems to be the idea according to the Mozilla Web Worker documentation for terminate():
If you need to immediately terminate a running worker, you can do so by calling the worker’s terminate() method… The worker thread is killed immediately without an opportunity to complete its operations or clean up after itself.
My take is that this is a bug, and I will talk to Mozilla about this.
Can’t restart a terminated Web Worker
In both Firefox and Safari, if you have called the terminate
method on a Web Worker, there’s no way to get it started again by calling postMessage
on it. Instead, you need to reinstantiate the Worker with the new Worker("filename.js")
again, which sort of defeats the idea of pausing/playing a Worker’s action. I don’t know if this is due to design or implementation, but if you craft your code cleverly, you can work around this.
Spreading the load
Despite the issues I think Web Workers are great, and they could prove fantastic in terms of relieving the user interface from being frozen when having to do complex calculations and similar.
I forgot to mention, *don't* use workers if the user is expected to wait, i.e. if the users say: render me some big complicated image – a single thread will go faster.
*Do* use workers when the user should be able to continue with the application, i.e. in Bespin, they're using Web Workers to do the syntax highlighting, which you *wouldn't* want to block your code editing whilst you're using the app.
Nice introduction to Web Workers, Robert! Having workers is going to be awesome when all browsers start supporting them.
Also check out these two cool libraries for Web Workers:
– pmrpc, which is a library for RPC comunication with workers so that you don't have to write all that boilerplate code every time – http://bit.ly/JMtkm (it's also a library for inter-window RPC, based on postMessage)
– metaworker, which is a library that enables a mapreduce computation model via workers – http://bit.ly/7oTuS5
In many other environments, terminating a thread is deprecated and should only be used as a last resort. The nice way is to post a "please terminate yourself" message to the thread so it can shut down cleanly. Perhaps that is a best practice with web workers as well.
Beware of a bug in Chrome (see my bug report) that limits the number of workers that you can create. You can't create more than 16 workers in Chrome even if you try to terminate workers when you are finished with them. I.e. you can only call new Worker() 16 times. So pooling the workers seems like a good idea.
Remy,
Oh, absolutely – different approaches for different scenarios.
Ivan,
Thanks! Also, appreciate the tips!
Martin,
I guess so, but at the same time, in a web page/web application is crucial and I believe there are definitely use cases when one wants to terminate something immediately, no matter what.
Interesting about the bug in Google Chrome, thanks!
Does anybody know what about IE – will it be implemented somewhen? I only found a script that emulates web workers in IE but that’s rather slow.
Chris,
No idea about IE, actually.
[…] […]
[…] want to replace html5 Web Workers, no way, I'm joking, but is another option to relay the execution of the javascript not in the […]
[…] For more details, refer to Robert Nyman’s post on web workers. […]
[…] node.js, que facilitaba las cosas frente a por ejemplo, Apache, pero ciertas preocupaciones sobre seguridad y un bug han hecho que en el lado cliente la cosa vaya lenta. Aún así, el olvidarse de cosas como […]
[…] web workers for background […]
Your web worker sample would work much better if you change the JavaScript file to this. As it is now you pump postMessages every iteration in the loop.
onmessage = function (evt) {
for (var i=evt.data, il=100000001; i<il; i++) {
if(i%100000==0 || i==evt.data)
postMessage(i);
}
}
Hasse,
Yes, but that was the point: to show it counting up and to test that while it’s working and updating it, the user interface doesn’t freeze (i.e. you can select text and more).
[…] web workers for background […]