Home Developing HTML5 Applications for AppUp – Part 2: Web Workers

Developing HTML5 Applications for AppUp – Part 2: Web Workers

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 at 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.pngto satisfy 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 it’s identifier primeNumber. We get the current time and write it into the output tag field that we defined in main.html, using it’s 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 &lt;= 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 &amp;&amp; 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 &lt; 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 &lt; 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:

Figure 1: The Running Application
As there is 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

 
Source Intel AppUp(SM) Developer Program

About ReadWrite’s Editorial Process

The ReadWrite Editorial policy involves closely monitoring the tech industry for major developments, new product launches, AI breakthroughs, video game releases and other newsworthy events. Editors assign relevant stories to staff writers or freelance contributors with expertise in each particular topic area. Before publication, articles go through a rigorous round of editing for accuracy, clarity, and to ensure adherence to ReadWrite's style guidelines.

Get the biggest tech headlines of the day delivered to your inbox

    By signing up, you agree to our Terms and Privacy Policy. Unsubscribe anytime.

    Tech News

    Explore the latest in tech with our Tech News. We cut through the noise for concise, relevant updates, keeping you informed about the rapidly evolving tech landscape with curated content that separates signal from noise.

    In-Depth Tech Stories

    Explore tech impact in In-Depth Stories. Narrative data journalism offers comprehensive analyses, revealing stories behind data. Understand industry trends for a deeper perspective on tech's intricate relationships with society.

    Expert Reviews

    Empower decisions with Expert Reviews, merging industry expertise and insightful analysis. Delve into tech intricacies, get the best deals, and stay ahead with our trustworthy guide to navigating the ever-changing tech market.