JavaScript Web Workers
Written by Ian Elliot   
Wednesday, 05 September 2012
Article Index
JavaScript Web Workers
Communication

A sure sign that you are writing a web app, rather than just a web page, is when your JavaScript brings the whole show to a stop. The problem is that, unless you make use of additional threads, JavaScript can only do one thing at a time. We look at Web Workers - the safe and easy way to create multi-threaded JavaScript.

This is a general version of the WinRT/WinJS specific article: WinRT JavaScript - Web Workers & Promises. A second article will deal with using jQuery to implement promise based Web Workers.

 

The JavaScript run environment is by default single threaded. This means that there is just one thing being done at any given time. Given a reasonably fast processor you might think that this wouldn't cause any problems, because any work you want to do is lightweight enough not to have an impact on the system, so why should it have an impact on the JavaScript run time environment? 

The reason that you can appear to  run out of processing power when there is still plenty available is that JavaScript only only has a single thread of execution that thread is the also the UI thread.

In other words the single thread is in a sense shared between responding to the UI and executing your code. For example, is a web page has been loaded and all of the initial JavaScript has been executed the UI thread isn't being used by your script and so all it has to do is wait for the user to click a button or cause an event in some other way. As soon as the event occurs your event handler gets to run and it is the UI thread that executes the instructions. While the UI thread is running your event handler nothing else can happen. The user can attempt to click buttons or enter text but nothing will happen until your event handler finishes and releases the UI thread to service events again. 

Normally the code in an event handler works so quickly that it releases the UI thread so fast that the user doesn't notice the slight pause in things happening. This is fine and nothing to worry about - don't seek solutions to problems that don't exist. However if you have a computation or task that is going to take a noticeable amount of time - even big fractions of a second then the user will notice that the UI goes dead while your event handler runs.

In this case you do have a problem and the solution is to use a Web Worker. 


A Slow Calculation

First let's start off with an example of a long computational task just asking to be implemented on a separated thread.  For the sake of simplicity the calculation is to sum a large number of integers and return the total.

function mySlowCalculation() {
     var total = 0;
     for (var i = 0; i <= 1000000000; i++) {
          total += i;
     };
     return total;
  };

Of course in practice your task could be doing all sorts of interesting things that take a long time.

You may also have to adjust the size of the for loop to take account of how fast your machine is. Notice that if you make the loop very long then the browser might just assume that your web page has crashed and offer to kill it for you. Just ignore this message. 

To see this in action before you have moved it off to another thread add a button with id Button1 and perhaps a second button that does nothing that you can attempt to click to prove that the UI is frozen.

<button id="Button1">Click to start </button>
<button>dummy button</button>

Now we can call the slow function when the button is clicked and to show that it has completed its work we can set the button's text to the result:

Button1.addEventListener("click", function (e) {
   Button1.textContent = mySlowCalculation();
  });

 

If you run this you will discover that when you click the button nothing works until the answer comes back from the function and is displayed on the button. If it all happens too quickly or slowly then change the number of loops.

Clearly this freezing of the UI is not good and this is the reason we need to use a separate thread for the calculation - or any task that takes similar amounts of time. 

That is we need a Web Worker.

Basic Web Worker

The good news is that Web Worker is very easy to use.

What is slightly difficult to get to grips with  is working out what you are not allowed to do and achieving simple communication between the threads.

Ideally you should wrap any Web Worker tasks you create as promises to make the code easier to use. We will take a look at how to do this using jQuery in another article - for the moment let's concentrate on using Web Workers raw.

There really is only one key object when using Web Workers, the Worker object. 

The worker object automatically starts a new thread and begins executing JavaScript code as soon as it is created.  

The basic action is that it loads the JavaScript file that you specify in its constructor and starts the script executing on a new thread.

So for example, if you have a program stored in myScript.js the instruction:

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

The use of "new" is optional

Although this is simple there is a subtly that you need to get clear if you are to avoid making silly mistakes. 

When you create a Worker object two things happen.

  1. the code stored in myScript.js or whatever file you specify is loaded and set running using a new OS level thread. 
  2. A Worker object is created on the UI thread and this is an object that your "standard" JavaScript code running on the UI thread can work with.

If you think that this is obvious and doesn't need to be said, so much the better.

Banner

The Trouble With Threads

If you have looked at the problem of writing multi-threaded programs this is where you might be getting worried. Starting a new thread so easily seems to be and easy way to do something dangerous.  However Web Workers have been implemented in a way that restricts the way that you use them to make them safe. At first these restrictions might seem tough to live with but after a while you realize that they are perfectly reasonable and don't really stop you from doing anything. 

The main simplification about threading with Web Workers is that the the new thread cannot share anything with the UI thread. 

The new thread cannot access any objects that the UI Thread can. This means it cannot interact with any global objects and it cannot interact with the DOM or the user interface in any way. The new thread runs in a little world of its own - but don't panic it can communicate with the UI thread in a very simple way. 

This inability to share objects  may seem a little restrictive but it is a restriction that is necessary to make sure that the two threads you now have don't try to access the same object at the same time.

If this was allowed to happen you would need to introduce a lot of complicated machinery - locks, semaphores and so on - to make sure that the access was orderly and didn't give rise to any very difficult to find bugs - race conditions, deadlock and so on.

in other words the Web Worker has big restrictions so that you can use it without complication and without any danger.

For most purposes it is sufficient and hence very effective.

Web Workers do have access to all of the global core JavaScript objects that you might expect.

They can also access some functions that are normally associated with the DOM - XMLHttpRequest() and setInterval etc.

The rule is that if the object could be shared in anyway with the UI thread then you cannot get access to it and this is a condition that is obviously satistifed for all of the core JavaScript objects and the DOM object that are allowed.

To make up for this restriction there are two new objects that the Web Worker can access - WorkerNavigator and WorkerLocation. The navigator provides basic information about the app's context - the browser in use, appName, appVersion and so on. The location object provides details of where the app is in terms of the current URL

if these two objects don't provide enough information you can easily arrange to pass the worker thread additional data of your choosing.

 



Last Updated ( Wednesday, 05 September 2012 )