Using HTML5 Web Workers to Create Dedicated Threads in Javascript

What are Web Workers in HTML5?
Web Workers are new feature in HTML5 allowing you to launch background threads in Javascript, a very useful feature if you need to perform some CPU intensive tasks. Normally, Javascript will execute all the code in a single thread, and that can not only slow down your page, but also render it completely unresponsive. By using a Web Worker, you can ensure that all the tasks that use too much CPU are executed in a separate parallel thread, while the page remains responsive to user actions.

The Anatomy of How Web Workers Work
If you ever had an experience with another programming language, chances are you are familiar with how threading works and surely OOP. HTML5 introduces a new Web Worker class, called Worker, whose constructor takes – no, not a function as you may expect – but a javascript file. Think of that file as a separate entity, or object. The communication between the two involves the postMessage function and the onmessage event. The former posts a message from either the web worker to the file and the former receives it either on the worker end or the file end. This mechanism provides asynchronous messaging mechanism between the two running in a separate thread.

Restrictions of Web Workers
Web workers execute independently of the browser UI and thus you will not have access to the DOM or any global variables you defined from the javascript file given to the web worker constructor. Your file “object” is completely isolated from that however, you can use the data property of the event to pass information back and forth.

Hung Page Example, Old Style
Now, let’s look at a classic javascript example that will render the page unresponsive. The page presents two buttons, Start Counting and Stop Counting and an area to show the current counter. When the user presses the Start Counting button, a loop is started increasing the counter and a <div> element is updated with its value. If the Stop Counting button is pressed, a flag variable is updated and the loop terminates. Here is the code – try to run it and the page will hang because it is running in a single thread, preventing you from taking any action and clicking on Stop Counting button:

<!DOCTYPE html>
<html lang="en-us">
<head>
	<meta charset="utf-8" />
	<title>Regular Javascript Example</title>
	<style type="text/css">
		body, input, div { font-family: Verdana; font-size: 11px; font-weight: bold; }
		input { margin: 0 16px 0 0; }
		#counter { background: #ffffaa; border: 1px solid #0000ff; display: inline-block; margin: 16px 0 0 0; }
		#counter-text { background: #000080; color: #ffff00; padding: 4px 16px; text-align: center; }
		#counter-data { color: #0000ff; padding: 16px; text-align: center; }
	</style>
	<script type="text/javascript">
	//<![CDATA[
	var currentCounter = 0;
	var stopped = false;
	function $(id) { return document.getElementById(id); }
	function startCounting()
	{
		$('buttonStart').disabled = true;
		$('buttonStop').disabled = false;
		currentCounter = 0;
		stopped = false;
		doCounting();
	}
	function stopCounting()
	{
		stopped = true;
	}
	function doCounting()
	{
		while (!stopped)
		{
			currentCounter++;
			$('counter-data').innerHTML = currentCounter;
		}
		$('buttonStop').disabled = true;
		$('buttonStart').disabled = false;
		$('counter-data').innerHTML = '-';
	}
	//]]>
	</script>
</head>
<body>
	<div>
		<input type="button" value="Start Counting" id="buttonStart" onclick="startCounting();" />
		<input type="button" value="Stop Counting" id="buttonStop" disabled="disabled" onclick="stopCounting();" />
	</div>
	<div id="counter">
		<div id="counter-text">Current Counter</div>
		<div id="counter-data">-</div>
	</div>
</body>
</html>

HTML5 Web Worker Example
Now, with HTML5, we can keep the UI responsive and the counter actually updating. First, let’s move the code that updates the counter into a separate file:

var currentCounter = 0;
var stopped = false;

function doCounting()
{
	while (!stopped)
	{
		currentCounter++;
		this.postMessage(currentCounter);
	}
	this.postMessage('-');
}

this.onmessage = function(e)
{
	switch (e.data)
	{
		case 'start':
			this.doCounting();
			break;
		case 'stop':
			stopped = true;
			break;
	}
}

As you can see from this code, the file itself does sort of define a class. The onmessage event will be triggered when the associated web worker calls postMessage function and we then can use data passed to identify what is it we would like to do. Note that local to the file variables are still ok to use. So, let’s see the modified HTML for this:

<!DOCTYPE html>
<html lang="en-us">
<head>
	<meta charset="utf-8" />
	<title>Regular Javascript Example</title>
	<style type="text/css">
		body, input, div { font-family: Verdana; font-size: 11px; font-weight: bold; }
		input { margin: 0 16px 0 0; }
		#counter { background: #ffffaa; border: 1px solid #0000ff; display: inline-block; margin: 16px 0 0 0; }
		#counter-text { background: #000080; color: #ffff00; padding: 4px 16px; text-align: center; }
		#counter-data { color: #0000ff; padding: 16px; text-align: center; }
	</style>
	<script type="text/javascript">
	//<![CDATA[
	var worker = null;
	function $(id) { return document.getElementById(id); }
	function startCounting()
	{
		$('buttonStart').disabled = true;
		$('buttonStop').disabled = false;
		var worker = new Worker('web-worker.js');
		worker.onmessage = function(e)
			{
				$('counter-data').innerHTML = e.data;
			}
		worker.postMessage('start');
	}
	function stopCounting()
	{
		alert(1);
		worker.postMessage('stop');
		$('buttonStop').disabled = true;
		$('buttonStart').disabled = false;
		worker = null;
	}
	//]]>
	</script>
</head>
<body>
	<div>
		<input type="button" value="Start Counting" id="buttonStart" onclick="startCounting();" />
		<input type="button" value="Stop Counting" id="buttonStop" disabled="disabled" onclick="stopCounting();" />
	</div>
	<div id="counter">
		<div id="counter-text">Current Counter</div>
		<div id="counter-data">-</div>
	</div>
</body>
</html>

You would notice that on pressing the Start Counting button we now instantiate a Worker object, send a 'start' message that is trapped in our javascript file, and define the onmessage event that updates the counter on the page when a message is received back. The Stop Counting button now sends a 'stop' message that is trapped in the onmessage in our file and sets the flag to break the loop.

However, if you try to run this code you would find yourself trapped – your browser UI is going to work just find, you would be able click on buttons and enter text in textboxes, but the counter will keep running. This is because you have to explicitly terminate the thread once it’s started. So, slight modifications are in order to the code above. The terminate function of the Worker object does just that. So, there is no need for a 'stop' action, all we need is to terminate the thread when the Stop Counting button is clicked. Here is the final code you can run:

web-worker.js

this.currentCounter = 0;

function doCounting()
{
	while (true)
	{
		this.currentCounter++;
		this.postMessage(this.currentCounter);
	}
	this.postMessage('-');
	this.terminate();
}

this.onmessage = function(e)
{
	switch (e.data)
	{
		case 'start':
			this.doCounting();
			break;
	}
}

web-worker.html

<!DOCTYPE html>
<html lang="en-us">
<head>
	<meta charset="utf-8" />
	<title>Regular Javascript Example</title>
	<style type="text/css">
		body, input, div { font-family: Verdana; font-size: 11px; font-weight: bold; }
		input { margin: 0 16px 0 0; }
		#counter { background: #ffffaa; border: 1px solid #0000ff; display: inline-block; margin: 16px 0 0 0; }
		#counter-text { background: #000080; color: #ffff00; padding: 4px 16px; text-align: center; }
		#counter-data { color: #0000ff; padding: 16px; text-align: center; }
	</style>
	<script type="text/javascript">
	//<![CDATA[
	var worker = null;
	function $(id) { return document.getElementById(id); }
	function startCounting()
	{
		$('buttonStart').disabled = true;
		$('buttonStop').disabled = false;
		worker = new Worker('web-worker.js');
		worker.onmessage = function(e)
			{
				$('counter-data').innerHTML = e.data;
			}
		worker.postMessage('start');
	}
	function stopCounting()
	{
		worker.terminate();
		$('counter-data').innerHTML = 'Stopped at ' + $('counter-data').innerHTML;
		$('buttonStop').disabled = true;
		$('buttonStart').disabled = false;
		worker = null;
	}
	//]]>
	</script>
</head>
<body>
	<div>
		<input type="button" value="Start Counting" id="buttonStart" onclick="startCounting();" />
		<input type="button" value="Stop Counting" id="buttonStop" disabled="disabled" onclick="stopCounting();" />
	</div>
	<div id="counter">
		<div id="counter-text">Current Counter</div>
		<div id="counter-data">-</div>
	</div>
</body>
</html>

Enjoy and if you have questions, don’t be shy to ask!

This entry was posted in HTML5 and tagged , , , , , , , , , , . Bookmark the permalink.

2 Responses to Using HTML5 Web Workers to Create Dedicated Threads in Javascript

  1. Thank you! I have tried with the help of tutorials I found thanks to good ol’ google before and it was a giant mess. Maybe because I was switching tabs?
    Who knows … in any case, this will make it easy and I may become an obnoxious overuser of this posting now. Thanks, Lauren!Nice experiment!
    I’m impatient to see the long-term effect on this one 🙂 Will you post more info after a month-two-three?

  2. Ronaldcow says:

    From these findings?

Leave a Reply

Your email address will not be published. Required fields are marked *