Learn HTML5 - Part 2: Web Workers

Introduction

Continuing from my previous article "Learn HTML5 - Part 1: Introducing HTML5 APIs", this article explains Web Workers that run scripts in the background independently of any user interface scripts.

You might be annoyed that HTML5 applications are written using JavaScript and when we are working with JavaScript the biggest limitation we have is, it will freeze the main window, simply blocking the “UI Thread” and you can't interact with your application any more when any long-running process is performed. Why? Just because each JavaScript execution process exists inside a unique thread, because it blocks the UI thread and shows alerts to users.

The following are are some message examples.

Chrome



Firefox



IE



Let's use an example to illustrate the nature of our tasks, where the application is not able to do multi-threaded tasks.

Suppose that when you start an application the init() function is first executed and assume it takes 15 ms to complete the execution and before that if you raise a mouse click event then at just after 5ms it is placed into the queue to be processed later on because the main thread is busy in the init() execution.

Then let us suppose you have already scheduled a timerTask() that starts automatically after 15ms when the application is started then this function should logically be executed at the 15ms timeframe and now when the main thread is free, it can start to dequeue the saved requests. Since the first request is for handling the mouse click event then after the timerTask() executes, that is the problem, the timerTask() function that was scheduled to be run on the 15ms timeframe is slightly shifted. And so on if the user continues making a request at the same time, resulting in the error occuring as you saw in the images above.

This is what Web Workers were made for.

Web Workers

Web Workers APIs provide a way in JavaScript to run something in the background that can do tasks without interfering with the user interface. As per the W3C standard "It is a JavaScript script executed from an HTML page that runs in the background, independently of other user-interface scripts that may also have been executed from the same HTML page. Web workers are able to utilize multi-core CPUs more effectively."

Types of Web Workers

  • Dedicated workers
  • Shared workers

Dedicated workers

  • A dedicated worker is accessible from the parent script that created it
  • Wide browser support: All
  • It is simply tied to its main connection

Shared workers

  • A shared worker can be accessed from any script of the same origin.
  • Limited browser support: Chrome 4.0+, Safari 5.0+ and Opera 10.6+.
  • It can work with multiple connections.

Basically Web Workers work in the following three steps:

  • First it should be executed on separate threads
  • It should be hosted in separate files from the main page
  • Finally a Worker object needs to be instantiated to call them

If I were to say in a single line how to start working with Web Workers then simply you just need to create a new Worker object in your main page and call the Worker constructor and pass the URI of the Worker script as in the following:

var worker = new Worker('MyWorker.js');

After creating the worker, start it by calling the postMessage() method:

worker.postMessage()

Now what next? Next is, how to communicate through your worker. As I mentioned above postMessage() is used to start the worker and it only accepts a single argument, a string or JSON object depending on the browser.

In the following code snippet I pass 'Hello World' to a worker and the worker simply returns the message.

var worker = new Worker('MyWorker.js');
worker.addEventListener('message', function (e) {
   console.log('Worker says: ', e.data);
   }, false);
worker.postMessage('Hello World');

As you saw I specified MyWorker.js when the worker is created, so what does this js file do?

The MyWorker.js file is our main worker that handles the message. Now, you can see in the following code snippet a self reference of the global scope for the worker and messages are received using addEventListener() as in the following:

self.addEventListener('message', function (e) {
self.postMessage(e.data);
}, false);=90

Let's use an example to learn how to exactly work with Web Workers.

Here I am creating an application to pass messages using JSON objects.

First create an HTML5 application and design depending on your requirements, I do something like:


Use the following code for the design:

<button id="Start" onclick="start()">Start Worker and Send Message</button>
<button id="Stop" onclick="stop()">Stop Worker</button><br />
<br />
<output id="Message"></output>

Then create two functions for starting and stopping the worker

function start() {
   worker.postMessage({ 'cmd': 'start', 'msg': 'Hi this is Web Worker Demo App' });
   }
function    stop() {
   worker.postMessage({ 'cmd': 'stop', 'msg': 'Thank You' });
}

And then create and start the worker as in the following:

var worker = new Worker('MyWorker.js');
worker.addEventListener('message', function (e) {
   document.getElementById('Message').textContent = e.data;
}, false);

Now create a new js file; I named here MyWorker.js, in this js file define the cases inside the addEventListener() method to handling start and stop process of the worker.

var data = e.data;
switch (data.cmd) {
   case 'start':
      self.postMessage('Worker Started :' + data.msg);
   break;
   case 'stop':
      self.postMessage('Worker Stopped :' + data.msg);
self      .close();
break   ;
};

Done, the following is my complete code.

Demo.html

<!DOCTYPE html>
<html>
<head>
    <title>Web Worker Demo App</title> 
    <script type="text/javascript">
        function start() {
            worker.postMessage({ 'cmd': 'start', 'msg': 'Hi this is Web Worker Demo App' });
        }       
        function stop() {           
            worker.postMessage({ 'cmd': 'stop', 'msg': 'Thank You' });
        }
        var worker = new Worker('MyWorker.js');
        worker.addEventListener('message', function (e) {
            document.getElementById('Message').textContent = e.data;
        }, false);       
    </script>
</head>
<body>
    <div>
        <button id="Start" onclick="start()">Start Worker and Send Message</button>       
        <button id="Stop" onclick="stop()">Stop Worker</button><br />
        <br />
        <output id="Message"></output>
    </div>
</body>
</html>

 

MyWorker.js

self.addEventListener('message', function (e) {
    var data = e.data;
    switch (data.cmd) {
        case 'start':          
            self.postMessage('Worker Started :' + data.msg);
            break;       
        case 'stop':
            self.postMessage('Worker Stopped :' + data.msg);
            self.close();
            break;           
    };
}, false);

Now run the application and you can see output like as in the following image:



Now click on the first button that says "Start Worker and Send Message" and you can see your worker is started and passed the message.



Now click on the second button that says "Stop Worker" and you can see your worker is stopped and your first button is disabled for the time until you refresh your page.



Summary

Web Workers are just a way to architect your existing JavaScript code for parallel execution that helps your page's logic to avoid waiting for synchronization tasks.