HTML5 Background tasks using Web Workers
Wednesday 02 May 2012
Fortunately there is a fix for this in the form of the new HTML5 Web Workers specification
Web workers are relatively well supported, IE9 being the big exception, as you can see from caniuse.com. Getting started with them is quite straightforward, basically you create a Worker object and pass it the URL of the script you want to execute. This has to be a URL, you can’t just pass a function, something that would have been handy.
The following HTML page contains all the code required.
The important part is the start button:
Communicating with Web Workers
The code executing as part of the Worker is completely isolated from the UI document. You can send messages between the two using the postMessage() function but all data will be cloned, no shared references exist. When a message has been posted the other party can specify the onmessage callback and react to the message. The worker has restrictions to the data being passed, the most important one is that nothing related to the DOM can be passed to the worker object and everything that can be is cloned first.
A simple implementation of the worker to calculate prime numbers looks something like this. Note the postMessage being used to pass the computed prime numbers back to the user interface,
Controlling the workers execution
Suppose we want to stop the workers execution there is a terminate() function on the worker which is used in the Stop button.
This will kill the worker process and that is the end of it, there is no way to restart it. Nice, simple and effective however sometimes that is not quite what we want and we want to restart the worker.
Suspending and resuming a worker
Given the fact that we can send messages between the UI and the worker the code below might look like a valid approach. Unfortunately this doesn’t quite work
The problem here is that the postMessage()/onmessage pair works using the event loop and as long as the Web Worker is busy calculating it will never receive the message, instead it will be queued and remain queued until the worker is idle, something that doesn’t happen until all primes have been calculated.
Using a chunking algorithm
With this in place we can pause and resume our prime number calculation
The only problem with this approach is that, in some browsers, it slows down calculations a lot. So instead of doing a setTimeout() after every prime you might want to do so once after a series of numbers has been checked. For example with the code below:
How about browsers that don’t support Web Workers?
It turns out that using this chunking approach in the UI is a very decent alternative to using a Web Worker as it prevents the user interface from completely blocking. It will still be somewhat slower to respond but is the best you can do in the circumstances.