Home JavaScript Web Workers: Performing Calculations in Parallel

# Web Workers: Performing Calculations in Parallel

In this final tutorial on working with Javascript web workers, you will learn how to perform calculations in parallel.

Author Info:
Rating:  / 5
September 19, 2011

SEARCH DEVARTICLES

In this final tutorial on working with Javascript web workers, you will learn how to perform calculations in parallel.

If you’re a web designer interested in understanding the basic concepts surrounding the implementation of web workers in HTML5, then this article series is the one for you. Comprised of five tutorials, the series will teach you how to create, interact and manipulate a few web workers from a main web page and use these objects for performing mathematical algorithms in parallel.

Before we begin this final piece however, let's recap the topics that were covered in the last tutorial, in case you missed it or need a refresher. In the course of that installment I created a simple example, which demonstrated how easy it is to close (from a “commander” script) the threads associated to several different workers.

In simple terms, the process was reduced to simply sending out a custom string from the main script, which made the workers shut down their respective threads via the already familiar “close()” method.

Although exploring the messaging API that allows you to talk to workers is a must for anyone wanting to employ the objects in a truly useful way, there’s still an aspect of them that I haven’t covered in depth. The main advantage of a worker is its inherent ability for running processes independently from the browser’s UI thread, something that turns them into good candidates for executing resource-intensive calculations.

In light of this concept, in this final part of the series I’m going to illustrate how to use the functionality of some web workers to calculate (in parallel) the factorials of three arbitrary numbers. In doing so, you’ll realize how helpful workers can be when it comes to performing side-by-side recursive algorithms.

Stopping Multiple Workers from a Main Script: Reintroducing an Earlier Example

If for some reason you missed the prior chapter of the series, where I explained how to close the threads associated to a few web workers, below I include the source code corresponding to this example, so that you can analyze it and understand its underlying logic.

Here’s the page containing the script that instructs the workers to shut down their respective processes:

<!doctype html>
<html lang="en">
<meta charset="utf-8" />
<title>Example stopping multiple Web Workers</title>
<script>
// display messages from the workers
function displayMessage(msg) {
var section = document.getElementsByTagName('section')[0];
if (section === null){return};
var par = document.createElement('p');
par.setAttribute('class', 'worker_message');
par.appendChild(document.createTextNode(msg));
section.appendChild(par);
}

// create the web workers
var worker1 = new Worker('worker1.js');
var worker2 = new Worker('worker2.js');
var worker3 = new Worker('worker3.js');

// get messages from the web workers
worker1.onmessage = function(e) {
displayMessage(e.data);
}
worker2.onmessage = function(e) {
displayMessage(e.data);
}
worker3.onmessage = function(e) {
displayMessage(e.data);
}

// send some messages to the web workers
worker1.postMessage('Hello worker 1, how are you doing?');
worker2.postMessage('Hi worker 2, is everything fine over there?');
worker3.postMessage('Hey worker 3, keep doing what you are doing.');

// stop the workers
worker1.postMessage('QUIT');
worker2.postMessage('QUIT');
worker3.postMessage('QUIT');
</script>
<style>
body {
margin: 0;
background: #0c1e74;
font: 0.8em Arial, Helvetica, sans-serif;
color: #000;
}
h1 {
font-weight: normal;
font-size: 2.1em;
color: #0c1e74;
}
h2 {
font-weight: normal;
font-size: 1.8em;
color: #0c1e74;
}
p {
margin: 0 0 15px 0;
}
#wrapper {
width: 920px;
margin: 0 auto;
background: #fff;
}
display: block;
}
.worker_message {
color: #00f;
}
</style>
<body>
<div id="wrapper">
<h1>Using JavaScript Web Workers</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse auctor commodo risus, et ultrices sapien vestibulum non. Maecenas scelerisque quam a nulla mattis tincidunt. Etiam massa libero, pharetra vel laoreet et, ultrices non leo. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed posuere ullamcorper lacus et sollicitudin. Morbi ultrices condimentum lacus, sit amet venenatis purus bibendum sit amet.</p>
<section>
<h2>Worker message section</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse auctor commodo risus, et ultrices sapien vestibulum non. Maecenas scelerisque quam a nulla mattis tincidunt. Etiam massa libero, pharetra vel laoreet et, ultrices non leo. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed posuere ullamcorper lacus et sollicitudin. Morbi ultrices condimentum lacus, sit amet venenatis purus bibendum sit amet.</p>
</section>
<footer>
<h2>Footer section</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse auctor commodo risus, et ultrices sapien vestibulum non. Maecenas scelerisque quam a nulla mattis tincidunt. Etiam massa libero, pharetra vel laoreet et, ultrices non leo. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed posuere ullamcorper lacus et sollicitudin. Morbi ultrices condimentum lacus, sit amet venenatis purus bibendum sit amet.</p>
</footer>
</div>
</body>
</html>

In simple terms, once the web workers have been constructed and a few messages have been dispatched to them, the script finally sends out a “QUIT” command, which tells the objects to close their associated threads.

In fact, the syntax of the command is entirely arbitrary (you’re free to use the string that best suits your taste and preferences). This fact becomes even more evident if you study the definitions of the workers’ files. Check them out:

(worker1.js)

// get messages from the main script
self.onmessage = function(e) {
if (e.data == 'QUIT') {
self.postMessage('Web worker 1 says: stopping work right now...');
self.close();
}
}

// do some other stuff here

(worker2.js)

// get messages from the main script
self.onmessage = function(e) {
if (e.data == 'QUIT') {
self.postMessage('Web worker 2 says: stopping work right now...');
self.close();
}
}

// do some other stuff here

(worker3.js)

// get messages from the main script
self.onmessage = function(e) {
if (e.data == 'QUIT') {
self.postMessage('Web worker 3 says: stopping work right now...');
self.close();
}
}

// do some other stuff here

See how simple is to implement a bunch of workers that interpret the previous “QUIT” command?

So far, so good. Now that you grasped how the earlier example functions, it’s time to move forward and continue learning a few more useful things about web workers. In line with the concepts expressed in the introduction, in the next section I’m going to create an example similar to the prior one, only that this time the workers will be a little more active, as they will be responsible for calculating in parallel the factorial of three different numbers.

Implementing More Complex Workers: Calculating the Factorial of a Number

Considering that the last example that I plan to set up in this tutorial will use the functionality of web workers to execute (in parallel) a recursive function (in this case determining the factorial of a specific number, but it could be anything else), the main script that will interact with the objects will look quite similar to the one shown in the previous section.

Having clarified that, here’s the implementation of the script in question:

<!doctype html>
<html lang="en">
<meta charset="utf-8" />
<title>Executing in parallel multiple calculations using different Web Workers</title>
<script>
// display messages from the workers
function displayMessage(msg) {
var section = document.getElementsByTagName('section')[0];
if (section === null){return};
var par = document.createElement('p');
par.setAttribute('class', 'worker_message');
par.appendChild(document.createTextNode(msg));
section.appendChild(par);
}

// create the web workers
var worker1 = new Worker('worker1.js');
var worker2 = new Worker('worker2.js');
var worker3 = new Worker('worker3.js');

// get messages from the web workers
worker1.onmessage = function(e) {
displayMessage(e.data);
}
worker2.onmessage = function(e) {
displayMessage(e.data);
}
worker3.onmessage = function(e) {
displayMessage(e.data);
}
</script>
<style>
body {
margin: 0;
background: #0c1e74;
font: 0.8em Arial, Helvetica, sans-serif;
color: #000;
}
h1 {
font-weight: normal;
font-size: 2.1em;
color: #0c1e74;
}
h2 {
font-weight: normal;
font-size: 1.8em;
color: #0c1e74;
}
p {
margin: 0 0 15px 0;
}
#wrapper {
width: 920px;
margin: 0 auto;
background: #fff;
}
display: block;
}
.worker_message {
color: #00f;
}
</style>
<body>
<div id="wrapper">
<h1>Using JavaScript Web Workers</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse auctor commodo risus, et ultrices sapien vestibulum non. Maecenas scelerisque quam a nulla mattis tincidunt. Etiam massa libero, pharetra vel laoreet et, ultrices non leo. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed posuere ullamcorper lacus et sollicitudin. Morbi ultrices condimentum lacus, sit amet venenatis purus bibendum sit amet.</p>
<section>
<h2>Worker message section</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse auctor commodo risus, et ultrices sapien vestibulum non. Maecenas scelerisque quam a nulla mattis tincidunt. Etiam massa libero, pharetra vel laoreet et, ultrices non leo. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed posuere ullamcorper lacus et sollicitudin. Morbi ultrices condimentum lacus, sit amet venenatis purus bibendum sit amet.</p>
</section>
<footer>
<h2>Footer section</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse auctor commodo risus, et ultrices sapien vestibulum non. Maecenas scelerisque quam a nulla mattis tincidunt. Etiam massa libero, pharetra vel laoreet et, ultrices non leo. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed posuere ullamcorper lacus et sollicitudin. Morbi ultrices condimentum lacus, sit amet venenatis purus bibendum sit amet.</p>
</footer>
</div>
</body>
</html>

As can be seen above, the only tasks that the main script performs is to spawn three workers and start listening to them via the familiar “onmessage” handler. If a worker sends something back to the page, the data will be echoed to the screen. So far, nothing interesting is happening here, right?

Well, it’s time to pay attention to the workers’ side! Since they’ll be tasked with calculating the factorial of a few numbers, the next step that must be taken is to show the definitions of the objects’ associated files, so that you can grasp how they will function.

Getting the Prior Example Completed: Defining the Workers’ Associated Files

What’s the “wildcard” example used nearly everywhere for explaining recursion? Yes, calculating the factorial of a number! I'll skip the discussion on recursion, however, I’m going to be instructing the three earlier web workers to execute a factorial algorithm (in parallel) for different arbitrary numbers.

The purpose of this is simply to demonstrate that web workers are capable of doing multithreading, apart from sending to - and getting messages from - a main script.

Here are the files corresponding to the pertaining workers. Here they are:

(worker1.js)

// calculates the factorial of a given number
function factorial(n) {
if (n == 0){return 1};
return n * factorial(n-1);
}
// calculate the factorial of 20 and send the result to the main script
var n = factorial(20);
self.close();
self.postMessage('Worker 1 says: the factorial of 20 is ' + n);

(worker2.js)

// calculates the factorial of a given number
function factorial(n) {
if (n == 0){return 1};
return n * factorial(n-1);
}
// calculate the factorial of 50 and send the result to the main script
var n = factorial(50);
self.close();
self.postMessage('Worker 2 says: the factorial of 50 is ' + n);

(worker3.js)

// calculates the factorial of a given number
function factorial(n) {
if (n == 0){return 1};
return n * factorial(n-1);
}
// calculate the factorial of 100 and send the result to the main script
var n = factorial(100);
self.close();
self.postMessage('Worker 3 says: the factorial of 100 is ' + n);

Each worker is responsible for determining the factorial of a given number (in this case my list included the values 20, 50 and 100, but they can be replaced by others). With the objects already set, the last thing left to do is to launch the browser and test the main web page.

Effectively, each recursive function has been invoked in parallel and the resulting values have been returned independently to the main script.

Of course, this doesn’t mean that workers must be that boring all the time. So, fire up your spark of creativity and make your own workers perform more engaging tasks. After all, now that you know how to achieve parallelism in JavaScript, it’s up to you to fully exploit this shiny feature.

Closing Thoughts

It’s hard to admit it, but we’ve come to the end of this series. But, there’s no need to feel down, as in its subsequent parts you learned (or at least that was my humble intention) what web workers are and how to take advantage of their functionality in a few concrete use cases.

Quite possibly, this is only a biased opinion, but I believe that web workers along with web sockets (http://en.wikipedia.org/wiki/WebSockets) are one of the most useful features bundled with HTML5. Naturally, there’s still a long road to walk until we’ll see a mature, true cross-browser support for this new race of JavaScript objects. But who knows? Maybe the future will bring us some unexpected good news in this field. As always, time (and some browser vendors) will tell.

See you in the next web design tutorial!

 DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware.