Make Your UI More Responsive with HTML5 Web Workers

HTML5 Web Workers

Argh!!… Your web application has to sort a lot of data and you get the dreaded error message… “A script on this page may be busy, or it may have stopped responding…” Or maybe you’re writing some image processing code that takes forever on large images… Your UI is non-responsive. What are you going to do!!?… You could try to break up the work into small pieces and use timers to spread out the work. But what a PITA!

What if JavaScript had the ability to run your code in the background and not interfere with the responsiveness of your UI. What if you could spawn a thread to do long running computations such as image filtering and sorting. Well with HTML5 Web Workers you can do just that! In the rest of this article, I’m going to teach you how to use Web Workers within your own applications. Click here to try the sample app.

Web Workers are an HTML5 feature that allow a JavaScript developer to create additional threads of execution which can be used to execute long running code in the background. Normally your JavaScript runs on what is called the main UI thread. Everything you do from JavaScript including all DOM manipulations are executed sequentially on this single thread. If your JavaScript code runs too long without returning control to the browser, your UI will stop responding to the user. The following single line of code shows how to create a web worker:

    
// A webworker is created and the JavaScript file longjob.js
// is loaded into its context.
worker = new Worker("longjob.js");

This code creates a new web worker object that has its own JavasScript context running in its own thread, loads the referenced JavaScript URL into that context and executes it.  In the next code block, we’ll walk through this again giving a bit more context.


var worker;

function runInWebWorker() {
  // Does our browser support webworkers?
  if (!worker && window.Worker) {
    // A webworker is created and the JavaScript file longjob.js
    // is loaded into its context.
    worker = new Worker("longjob.js");
    if (worker) {
      // Register an event handler that will receive messages from our
      // web worker
      worker.addEventListener('message', function(e) {
        // Our webworker sends us a message when it is done.
        alert("longJob returned: " + e.data);
      });    
    }
    // Send a message to the worker to execute the longJob function
    // in the context of the workers thread.
    worker.postMessage(30);
  }
  else alert("Web Workers are not Supported in this Browser.");
}

Message passing is the mechanism used to communicate between a web worker and the main UI thread.  The web worker’s postMessage method is used to send messages to and from the web worker.  In order to receive a message an event handler must be registered with the addEventListener method.  In the code block above, after checking to see if the browser supports web workers and we’ve created the web worker, a ‘message’ event handler is registered on the web worker object.  This particular event handler is used by the main UI thread to receive messages from our web worker.  Next a call to postMessage is made to send a message in the other direction to our web worker.  In the sample, we pass a simple numeric value of 30 as the message payload (which will be the number of seconds that we want our job to run).  Note: You can send more complicated messages in the form of arrays or simple objects.  In the next code block, we’ll look at the code loaded into our web worker, the contents of longjob.js:


// longjob.js
// function to simulate a long running job
// loops for approximately n seconds.
function longJob(n) {
  var start = new Date(); 
  var elapsedSeconds = 0;
  while(elapsedSeconds < n) {
    var v = 0;
    for (var i = 0; i < 1000000; i++) v += i;
    var end = new Date();
    elapsedSeconds = (end.getTime() - start.getTime())/1000.0;
  }
  return "All Done in " + elapsedSeconds + " seconds.";
}

// Our webworker registers for an message event so we can talk
// to it from our main thread and ask it to do something.
self.addEventListener('message', function(e) {

  // Invoke the longjob function within our worker thread
  // and pass in the parameter that was sent in.
  var result = longJob(e.data);

  //  Send a message back to the main thread with the result
  self.postMessage(result);
  }, false);

The longJob function simulates a long running function by taking a numeric value n and looping until n seconds have transpired.  Next an event handler is registered with a call to self.addEventListener so that our web worker can receive messages sent to it from the main UI thread.  Note that within the context of our web worker the special variables this and self reference the web worker object itself.  The parameter passed into the postMessage API is made available as a data property (shown as e.data in code above) on the event object passed into the event callback function.  When longJob completes our event handler sends a message back to the main thread using the postMessage method.

Web workers can be a very useful and empowering feature but they do have some limitations. All communication into and out of your web workers is done by passing in and out data in the form of messages.  All data is passed by value meaning that any object references are converted to a serialized copy of the objects data.  You can’t directly access or manipulate the DOM from a web worker.  While this may seem somewhat limiting these restrictions pretty much eliminate all of the synchronization bugs that can arise when using threads.

Want more on Web Workers? Signup for my mailing list and I’ll send you a link to the updated code from my earlier article, “How You Can Do Cool Image Effects Using HTML5 Canvas”, that will now run the image filters in the background using Web Workers. Just drop your email in the form below!

I hope you’ve enjoyed learning about web workers. You can get the full source code for the article here and try out the sample app here.

11 Comments.

  1. Make Your UI More Responsive with HTML5 Web Wor... - pingback on April 23, 2013 at 1:19 am
  2. Do you have a practical example of what kind of situation you might want to do this? For instance something happening in a scroll event listener might slow the UI, would you recommend sticking code like that in a web worker? cheers

    • Web workers can’t directly manipulate the DOM. So they are really designed for situations where you can package up some data that you need to crunch on for a while without blocking your main UI. Image Filtering is a pretty solid example, where the pixel data for the images can be sent over to a web worker to do the processing and your users can continue to work on your app while that is happening in the background. Sorting large amounts of data for a list is another good example as long as you can separate the data for sorting from your DOM elements.

  3. I have an ajax call that can take up to 30s to fetch the data. Would it be possible to load that data in a worker?

    Great article

    Cheers

    • The XMLHTTPRequest should happen asynchronously and shouldn’t block your UI. Are you doing a lengthy operation on the data once you’ve received it? How much data are we talking about?

  4. Pie in the Sky (April 26, 2013) | MSDN Blogs - pingback on April 27, 2013 at 3:59 am
  5. Web links 07/05/2013 — Nevma Developers Blog - pingback on May 7, 2013 at 10:56 am
  6. HTML5 Web Workers for AJAX Requests | TechSlides - pingback on May 17, 2013 at 4:34 pm
  7. Get a dialog saying webworkers and not supported however I am using Chrome 29.

  8. Hi Oliver,

    If you’re loading the sample web page from the file system that’s likely your problem. There are a number of “security” restrictions when loading from the file system… You have to load it from a web server via http or if you really want to run it from your file system you can pass in a command line switch of ” –allow-file-access-from-files”.

    The same file is hosted here as well…
    http://www.storminthecastle.com/projects/webworkers/workers.html

    -John

Trackbacks and Pingbacks: