SHARE
Facebook X Pinterest WhatsApp

Introducing HTML 5 Web Workers: Bringing Multi-threading to JavaScript

thumbnail Introducing HTML 5 Web Workers: Bringing Multi-threading to JavaScript
Oct 20, 2011

It used to be that the only way to implement asynchronous behavior in JavaScript was to use methods like setTimeout() and setInterval(). Sometimes, the XMLHttpRequest object could be made to do the job. Event handlers are also asynchronous by nature.

Now, HTML5 is bringing us true multi-threading capability via Web Workers. These little fellas are ideal for running scripts in background threads, so that they don’t interfere with the user interface (UI). Let’s take a look at how to put Web Workers to good use.

When’s a Good Time to Use Them?

Any script that you want to execute in the background is a good candidate to run as a Web Worker. Think about cryptography, the sorting of large arrays, parallel processing, and I think you’ll see where Web Workers could be a big help.

Web Worker Limitations

Web Workers operate independently of the main browser UI thread so they’re not able to access many of its objects. One limitation is that Web Workers cannot access the DOM, so they can’t read or modify the HTML document. In addition, they can’t access any global variables or JavaScript functions within the main page. Finally, access to some objects, including the window, document, and parent, is restricted.

So what can you get at? Web Workers can access pretty much any object that is not directly related to the Document or main UI thread, including all the standard JavaScript data types, functions, and objects, such as strings, numbers, Date, Array, XMLHttpRequest, etc

An Example

There are some calculations that just never end; solving the value for PI is one of these. Although some claim to have solved it up to a billion decimal places, it still appears that the final value is nowhere in site. PI makes a great example for Web Workers for a couple of reasons. First, the calculation is simple, compared to a lot of other large numbers. The second reason is that it requires looping many, many times to get at some real accuracy, and that’s really processor intensive! Here’s the code that does the work:

var Pi=0, n=1, c=100000;
for (var i=0;i<=c;i++) {
  Pi=Pi+(4/n)-(4/(n+2));
  n=n+4;
}

I placed the code in an HTML test page to see what would happen. A text field allows me to enter a number for the loop count c variable:

<html>
<head>
<script type="text/javascript">
function CalculatePi()
{
    var loop = document.getElementById("loop");
    var c = parseInt(loop.value);
    var f = parseFloat(loop.value);
    var Pi=0, n=1;

    try {
      if (isNaN(c) || f != c ) {
        throw("errInvalidNumber");
      } else if (c<=0) {
        throw("errNegativeNumber");
      }
	
      for (var i=0;i<=c;i++) {
        Pi=Pi+(4/n)-(4/(n+2));
        n=n+4;
      }
      document.getElementById("PiValue").innerHTML = Pi;
    } catch (e) {
      var msg = "Input Error: ";
      if (e=="errInvalidNumber")
        msg += "Invalid number.";
      else if (e=="errNegativeNumber")
        msg += "Input must be positive.";
      else
        msg += e.message;
		    
        alert(msg);
    }
}
</script>
</head>
<body>
<label for="loop">Enter the number of cycles:</label>
<input id="loop" type="number" value="100" />
<input type="button" onclick="CalculatePi()" value="Calculate Pi" />
<br>
<br>
<div id="PiValue">PI value appears here</div>
</body>
</html>

I found that a number in the millions did two things: it resulted in a fairly accurate Pi value, and it slowed down the browser to a crawl! In fact, I received this message:

Slow Script dialog

Let’s try that again with a Web Worker.

The pi.js Script

So far, all I’ve done was move the CalculatePi() function into a separate script file and changed the line that sets the PiValue DIV, because the worker cannot access the DOM. Instead, I posted a message to the main thread using the code “self.postMessage({‘PiValue’: Pi});”:

function CalculatePi(loop)
{
    var c = parseInt(loop);
    var f = parseFloat(loop);
    var n=1;

    //these errors will need more work…
    if (isNaN(c) || f != c ) {
      throw("errInvalidNumber");
    } else if (c<=0) {
      throw("errNegativeNumber");
    }
	
    for (var i=0,Pi=0;i<=c;i++) {
      Pi=Pi+(4/n)-(4/(n+2));
      n=n+4;
    }
    self.postMessage({'PiValue': Pi});
}
//wait for the start 'CalculatePi' message
//e is the event and e.data contains the JSON object
self.onmessage = function(e) {
  CalculatePi(e.data.value);
}

Communicating With a Worker

The worker and the parent page communicate using messaging. Each can add a listener to the onmessage() event to receive messages from the other. As we saw in the pi.js script above, messages are sent via the postMessage() method. Depending on the browser, the message can be passed as either a string or JSON object. My advice is to stick with a JSON object as all the latest browsers that support Web Workers support it.

Back in the main page, we can add a onmessage() event handler to display the results in the DIV. There is another special event called onerror() to catch errors that are thrown from the worker:

<html>
<head>
<script type="text/javascript">
  function launchPiWebWorker() {
    var worker = new Worker('pi.js');
		
    worker.onmessage = function(e) {
      document.getElementById("PiValue").innerHTML = e.data.PiValue;
    };
    worker.onerror = function(e) {
      alert('Error: Line ' + e.lineno + ' in ' + e.filename + ': ' + e.message);
    };

    //start the worker
    worker.postMessage({'cmd':   'CalculatePi', 
                        'value': document.getElementById("loop").value
                      });
  }

</script>
</head>
<body>
<label for="loop">Enter the number of cycles:</label>
<input id="loop" type="number" value="100" />
<input type="button" onclick="launchPiWebWorker()" value="Calculate Pi" />
<br>
<br>
<div id="PiValue">PI value appears here</div>
</body>
</html>

More on Error Handling

It should be noted that the onerror() event only applies to native JavaScript errors and does NOT work for user-defined errors. There is only one means of communication between the worker and main thread available to us and that is the messaging interface. Hence, if we perform validation on the loop field value in the worker, then we must either handle the errors there as well, or use postMessage() to send information about the error to the main page. The first choice is rarely a good one, as neither the DOM or alert() method are accessible to the worker, which leaves us with option two. We can implement validation handling by adding a new “error” message type (the other being “PiValue”) and the error codes:

var c = parseInt(loop);
var f = parseFloat(loop);
var n=1;

if (isNaN(c) || f != c ) {
    postMessage({'type': 'error', 'code': 'errInvalidNumber'});
    return;
} else if (c<=0) {
    postMessage({'type': 'error', 'code': 'errNegativeNumber'});
    return;
}
//...at the end of the CalculatePi() function
//we have to add the 'data' message type to differenciate 
//from errors
self.postMessage({'type': 'data', 'PiValue': Pi});

Remember that, since at no time are we actually catching our validation errors, we must exit the function or the script will proceed to attempt to calculate Pi based on the loop field’s faulty value!

Here is the updated onmessage() event handler code to display both validation errors (in an alert) and the calculated PI value (in the “PiValue” DIV:

worker.onmessage = function(e) {
  var data = e.data;
  switch (data.type) {
    case 'error':
      var msg = 'Input Error: '
      switch (data.code) {
        case 'errInvalidNumber':
          msg += 'Invalid number.';
          break;
        case 'errNegativeNumber':
          msg += 'Input must be positive.';
          break;
      }
      alert(msg);
      break;
    case 'data':
      document.getElementById("PiValue").innerHTML = data.PiValue;
      break;
  }
};

Conclusion

After being a fixture in languages like Java for years, Web Workers have now made multi-threading in Web applications a reality. They are supported as of Opera 10.6, Safari 4.0, Chrome 11.0, Firefox 4.0 and are expected to be included in IE 10. But note that multi-threaded processes are difficult to debug and can cause some wacky behavior if your’re not careful, so proceed accordingly.

thumbnail Robert Gravelle

Rob Gravelle resides in Ottawa, Canada, and has been an IT guru for over 20 years. In that time, Rob has built systems for intelligence-related organizations such as Canada Border Services and various commercial businesses. In his spare time, Rob has become an accomplished music artist with several CDs and digital releases to his credit.

Recommended for you...

Best VR Game Development Platforms
Enrique Corrales
Jul 21, 2022
Best Online Courses to Learn HTML
Ronnie Payne
Jul 7, 2022
Working with HTML Images
Octavia Anghel
Jun 30, 2022
Web 3.0 and the Future Of Web Development
Rob Gravelle
Jun 23, 2022
HTML Goodies Logo

The original home of HTML tutorials. HTMLGoodies is a website dedicated to publishing tutorials that cover every aspect of being a web developer. We cover programming and web development tutorials on languages and technologies such as HTML, JavaScript, and CSS. In addition, our articles cover web frameworks like Angular and React.JS, as well as popular Content Management Systems (CMS) that include WordPress, Drupal, and Joomla. Website development platforms like Shopify, Squarespace, and Wix are also featured. Topics related to solid web design and Internet Marketing also find a home on HTMLGoodies, as we discuss UX/UI Design, Search Engine Optimization (SEO), and web dev best practices.

Property of TechnologyAdvice. © 2025 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.