Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Developing HTML5 Applications for Intel AppUp(SM) center - Part 2: Web Workers

12 Jan 2012 0  
This is the second article in a series of articles covering how to develop HTML5 applications for Intel AppUp (SM) center. This article will address a new feature of HTML5 called Web Workers. We'll present an application that illustrates the use of Web Workers, as well as some other HTML5 features s

Table of Contents

  1. Introduction
  2. Web Workers
  3. The Problem
  4. Code Descriptions
  5. Other Applications
  6. Conclusions

Introduction

In this second installment of our series on developing HTML5 applications for AppUp, we'll look at a new feature of HTML5, Web Workers. We'll present an application that illustrates the use of Web Workers, as well as some other HTML5 features such as Canvas. We conclude with a discussion of other possible applications for Web Workers.

All of the source files described in this article can be downloaded from here.

Web Workers

Normally JavaScript code running in a browser executes in a single thread. If you need to perform some type of CPU intensive calculations, this can result in the JavaScript code running slowly and affecting the responsiveness for the user. Being able to run some JavaScript code in another thread or background process would solve this problem. Furthermore, modern multicore CPUs can execute multiple threads in parallel, so we would like to support multiple threads to make use of the hardware's capabilities.

Web Workers is a new HTML5 feature that supports multiple threads of execution. A Web Worker is a thread which allows you to run tasks in a background process in parallel with the main browser UI thread. A Web Worker is a JavaScript file which contains the task you want to perform in a separate thread. Web Workers allows you to do things like handle computationally intensive tasks without blocking the UI.

A common application of Web Workers is performing a computationally expensive task without interrupting the user interface. They can also be used for time consuming operations like network operations.

Multithreading by its very nature raises potential synchronization problems for shared data that is accessed by multiple threads. Because of this, Web Workers imposes some restrictions, most notably restricting access to the DOM, window object, document object, and parent object by Web Workers.

The example application in this article illustrates using Web Workers to perform a CPU intensive operation, specifically, calculating prime numbers.

The Problem

Recall that a prime number is a number greater than one that has no positive divisors other than one and itself. Prime numbers are important in many areas of computing such as encryption, but for our purposes it makes a good example of a CPU intensive calculation that can illustrate the use of Web Workers.

A simple method of determining if a number is prime is known as trial division. It consists of testing whether a given number n is a multiple of any integer between 2 and the square root of n. If the remainder after dividing n by the trial integer is zero, then n is not prime. There are algorithms that are much more efficient than trial division, but for our purposes it makes a good CPU-intensive problem to apply Web Workers to.

Our example application will compute prime numbers and display the most recently found prime. We'll also show the elapsed time since the calculations were started. As there is an infinite number of prime numbers, the program will never complete. We'll provide a Stop button to stop the calculations and a Close button to exit the application.

To show that the calculations are indeed happening in a separate thread, we will show an animation of a clock using the HTML5 Canvas feature.

Code Description

To start, we need a standard icon file named icon.png to satisfy the Intel AppUp™encapsulator.

We use a style sheet to get the look we want for buttons, the text font for the results, etc. You can study this file app.css at your leisure.

Execution starts with the file index.html. As shown in Listing 1, we include two JavaScript files: stopwatch.js and main.js:

<!DOCTYPE HTML>

<html>

    <head>

        <title>Web Workers</title>

        <link href="app.css" rel="stylesheet" type="text/css" />

        <script src="stopwatch.js" type="text/javascript"></script>

        <script src="main.js" type="text/javascript"></script>

    </head>
Listing 1: File index.html Part 1

Then we set a background image and call a JavaScript function to initially draw the stopwatch, and then declare some variables that we will be using:

<body background="numbers.jpg" onload="stopwatch();">

        <script type="text/javascript">

            var running = false;

            var worker;

            var timerId = 0;

            var seconds = 0;

            var date;

    </script>
Listing 2: File index.html Part 2

The remainder of the file displays the UI strings, defines a canvas that we will use to draw an animated stopwatch, and defines the Start, Stop, and Close buttons, associating them with the JavaScript functions to run when they are clicked. Note that the elapsed time and highest prime number results use <output> tags. We'll see shortly how they are hooked up to the code that produces the results.

<h1>Web Workers Example: Prime Numbers</h1>

    <hr>

    <h3>Press the Start button to start the calculation process.<br>

        Press the Stop button to stop calculating.</h3>

    <canvas id="stopwatch" width=150 height=150>Your browser doesn't support HTML5 canvas</canvas>

    <h3>Elapsed time: <output id="elapsedTime"></output><br>

            Highest prime number found: <output id="primeNumber"></output></h3>

        <hr>

        <br>

        <a href="#" id="Start" class="button white" onclick="startWorker()">Start</a>

        <a href="#" id="Stop" class="button white" onclick="stopWorker()">Stop</a>

    <a href="#" id="Close" class="button white" onclick="closeApplication()">Close</a>

  </body>

</html>
Listing 3: File index.html Part 3

Now let's look at the code for main.js, shown in Listing 4 below. The functions startWorker is called when the Start button is clicked. If the worker thread is not already running, it sets the variable running to true, elapsed time to zero, and saves the current date and time. It calls init() from stopwatch.js which we will look at later.

We then create a new Worker, the JavaScript Web Worker type. We add an event listener for it, and set the message handler to be the function e which we will see shortly. We set the events to go to the output tag we defined in main.html using its identifier primeNumber. We get the current time and write it into the output tag field that we defined in main.html, using its identifier elapsedTime. Finally, we post a message to the worker to get it started.

function startWorker()
{
    if (!running)
    {
        running = true;
        seconds = 0;
        date = new Date;
        init();

        worker = new Worker("worker.js");
        worker.addEventListener('message', onmessage, false);
        worker.onmessage = function(e) 
        {
            document.getElementById('primeNumber').textContent = event.data;
            date.setHours(0);
            date.setMinutes(0);
            date.setSeconds(seconds - 1);
            document.getElementById('elapsedTime').textContent = date.toTimeString("hh:mm:ss").substring(0, 8);
        }
        worker.postMessage(); // Start worker
    }
}

function stopWorker()
{
    if (running)
    {
        running = false;
        clearInterval(timerId);
        worker.terminate(); // Stop worker
    }
}  

function closeApplication()
{
    intel.adp.encapsulator.closeapplication()
}
Listing 4: File main.js

The stopWorker functions is called when the Stop button is clicked. It sets the variable running to false and stops a timer (which we created when init() was called, as we'll see later). It then terminates the Web Worker. This is important as there are resources that a Web Worker uses that are freed only when it is terminated. The closeApplication function, called by the Close button, simply closes the application.

Now let's look at the file worker.js, shown in listing 5. The message handler function in this file is run in the background by a separate thread from the main GUI JavaScript thread. It searches for prime numbers starting from 2 using the method of trial division. When a prime number is found, it posts a message passing the number as an argument. That gets written to the output field we set up in main web page.

addEventListener('message', onmessage, false);

onmessage = function(e) 
{
    var n = 1;
    search: 
    while (true)
    {
        n += 1;
        for (var i = 2; i <= Math.sqrt(n); i += 1)
            if (n % i == 0)
                continue search; // Number is not prime.
        // Number is prime, report it.
        postMessage(n);
    }
}
Listing 5: File worker.js

The final file is stopwatch.js, shown in Listing 6. The first function in the file is init. This was called from startWorker. It first calls the function stopwatch and then creates a timer that will call function stopwatch every second.

The other function in the file, stopwatch, draws an animated clock with a hand showing seconds. It uses the canvas element created earlier to draw. The location of the watch hand is drawn using the values of seconds that was earlier calculated based on the current time. The code is somewhat long but is straightforward. You can study it at your leisure.

function init()
{
    stopwatch();
    timerId = setInterval(stopwatch, 1000);
}

function stopwatch()
{
    var elem = document.getElementById('stopwatch');

    if (elem && elem.getContext)
    {
        var context = elem.getContext('2d');

        if (context)
        {
            context.save();
            context.clearRect(0, 0, 150, 150);
            context.translate(75, 75);
            context.scale(0.5, 0.5);
            context.rotate(-Math.PI / 2);
            context.strokeStyle = "grey";
            context.fillStyle = "white";
            context.lineWidth = 6;
            context.lineCap = "square";

            context.save();
            context.lineWidth = 5;
            for (i = 0; i < 60; i++)
            {
                if (i % 5 != 0)
                {
                    context.beginPath();
                    context.moveTo(120, 0);
                    context.lineTo(115, 0);
                    context.stroke();
                }

                context.rotate(Math.PI / 30);
            }

            context.restore();

            context.save();
            for (var i = 0; i < 12; i++)
            {
                context.beginPath();
                context.rotate(Math.PI / 6);
                context.moveTo(120, 0);
                context.lineTo(100, 0);
                context.stroke();
            }

            context.restore();
            context.fillStyle = "black";

            context.save();
            context.rotate(seconds++ * Math.PI / 30);
            context.lineWidth = 6;
            context.lineCap = "round";
            context.strokeStyle = "red";
            context.beginPath();
            context.moveTo(-30, 0);
            context.lineTo(100, 0);
            context.stroke();
            context.beginPath();
            context.arc(0, 0, 10, 0, 2 * Math.PI, false);
            context.fill();
            context.restore();

            context.beginPath();
            context.strokeStyle = "#417FA1";
            context.lineWidth = 10;
            context.arc(0, 0, 140, 0, 2*Math.PI, false);
            context.stroke();
            context.restore();
        }
    }
}
Listing 6: File stopwatch.js

Figure 1 shows a screen shot of the application running:

webworkers-screenshot1_0.png

Figure 1: The Running Application

As there are an infinite number of primes, the algorithm will never complete. Our example program will eventually fail when the values of the numbers it uses get so large that the JavaScript representation of a floating point number loses precision such that incrementing the value by one returns the same value. This won't happen until after many hours of execution.

Other Applications

For a CPU intensive application, you may think that using an interpreted language like JavaScript might be slow, but performance can be surprisingly good with modern JavaScript engines. As a very simple benchmark, I timed how long it took the the Web Workers application to calculate the primes up to 10 million. I compared that to a native C++ program that used the same algorithm to calculate primes. The HTML5 application took about 2 minutes to calculate the primes between 1 and 10 million. The C++ program took about 35 seconds on the same hardware. It was a somewhat unfair comparison because the HTML5 program also needed to perform output of the results in a nice format. Even so, the JavaScript code was still within the same order of magnitude of performance as the native code.

If you need to optimize performance using native code, one alternative is a hybrid application that has both browser (HTML5) code and native code. This, of course, will sacrifice portability.

Some other areas where Web Workers could be useful are the following:

  • performing network input/output in the background
  • rich text (e.g source code) syntax highlighting
  • image processing of data extracted from the <canvas> or <video> elements
  • updating a client-side database

Conclusions

In this article we introduced the HTML Web Workers feature. We showed a simple example application using Web Workers and discussed some more realistic real-life examples of where they could be applied.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here