Using Web Worker

Web worker

Limitations

Featuress

Using a dedicated Web worker

To use a dedicated Web worker, we ususally follow these steps:

In the Web worker thread

  1. Save the JavaScript code for Web worker thread in a seperate js file, eg., calculation.js;
  2. Once the Web worker thread is created by the main thread, it will run the JavaScript code right away. To wait for a signal from the main thread to start, in calculation.js, we can wrap the code in an event handler and wait for a message from the main thread: self.onmessage = function(event) { ... }. The data property of event argument contains the data passed from the main thread to Web worker thread;
  3. To send some message/result from the Web worker thread to the main thread, we call postMessage(...);
  4. Optinally, to terminate the Web worker thread itself, we can call close();

In the main thread

  1. In the HTML Web page or the JavaScript file for the main thread, use Worker() constructor to create the Web worker thread: var worker = new Worker('calculation.js');
  2. In the main thread, we need to set up an event handler to receive the message from the Web worker thread: worker.onmessage = function(event) { ... };
  3. If the Web worker thread is waiting for a message/signal from the main thread, then call worker.postMessage( ... );
  4. Optinally, to terminate the Web worker thread, we can call worker.terminate();

An example

This is the example adapted from [1]:

Source of prime.js. It is used to calculate the prime number in the background:

	self.onmessage = function(event) {
		findPrime();
	}
	 
	function findPrime()
	{
		var n = 1;
		var prime;
		while (true) {
			n++;
			prime = true;
			for (var i = 2; i <= Math.sqrt(n); i++)
				if (n % i == 0) {
					prime = false;
					break;
				}
			if (prime)  postMessage(n);		 
		}
	}
  

Source of findPrime.html. It is used to start and stop the Web worker thread:

	<!DOCTYPE html>
	<html>
		<head>
			<title>Find a Prime</title>
		</head>
		<body>
			<form>
				<label for="prime">The largest prime number: </label><input type="text" id="prime" disabled/> 
				<input type="button" value="Start" onclick="startFind()"/>
				<input type="button" value="End" onclick="stopFind()"/>
			</form>
			<script type="text/javascript">
			var worker;
		
			function startFind() {
				worker = new Worker('prime.js');
				worker.onmessage = function(event) {
					document.getElementById('prime').value = event.data;
				}
				worker.postMessage('Start');
			}
		
			function stopFind() {
				worker.terminate();
			}
			</script>
		</body>
	</html>
  

The screenshot of the Webpage:

Using a shared Web worker

To use a shared Web worker is similar to the dedicated Web worker, with the following differences:

An example

The source code of getServerTime.html:

	<!DOCTYPE html>
	<html>
		<head>
			<title>Get Server Time</title>
		</head>
		<body>
			<form>
				<label for="prime">The server's time: </label><input type="text" id="time" disabled/> 
				<input type="button" value="Get time" onclick="getTime()"/>
			</form>
			<script type="text/javascript">
			function getTime() {
				var worker = new SharedWorker('gettimeShared.js');
				worker.port.onmessage = function(event) {
					document.getElementById('time').value = event.data;
				}
				worker.port.postMessage('Start');
			}
			</script>
		</body>
	</html>
   

The source code of gettimeShared.js:

	self.onconnect = function(event) {
		var port = event.ports[0];
		var xhr = new XMLHttpRequest();
		xhr.open('get', 'gettime.php', true);
		xhr.onreadystatechange = function() {
			if (xhr.readyState == 4) {
				if ((xhr.status >= 200 && xhr.status<=299) || xhr.status == 304)
					port.postMessage(xhr.responseText);
				else alert('Error in ajax communication: ' + xhr.statusText);
			}
		};
		xhr.send(null); 
	};
   

The source code of gettime.php:

	<?php
		date_default_timezone_set('America/New_York');
		echo date('M d Y, h:i:s A');
	?>
   

The screenshot of the Webpage:

Using an embedded Web worker

It is possible to include the source code of the Web worker in the same HTML Web page. But we need to follow the following steps:

  1. Include the Web worker source code inside another <script> block, which can have any MIME type that is not "text/javascript". Examples include "text/js-worker" or "javascript/worker";
  2. In JavaScript code, first get the contents from that <script> block (i.e., the Web workersource code);
  3. Use Blob constructor to create a raw data representation of the Web worker source code;
  4. And then call window.URL.createObjectURL() to create a URL object and pass it to Worker() constructor;

The source code of findPrimev2.html:

	<!DOCTYPE html>
	<html>
		<head>
			<title>Find a Prime</title>
		</head>
		<body>
			<script id="worker" type="text/js-worker">
				self.onmessage = function(event) {
					findPrime();
				}
	 
				function findPrime()
				{
					var n = 1;
					var prime;
					while (true) {
						n++;
						prime = true;
						for (var i = 2; i <= Math.sqrt(n); i++)
							if (n % i == 0) {
								prime = false;
								break;
							}
						if (prime)  postMessage(n);		 
					}
				}			
			</script>
			<form>
				<label for="prime">The largest prime number: </label><input type="text" id="prime" disabled/> 
				<input type="button" value="Start" onclick="startFind()"/>
				<input type="button" value="End" onclick="stopFind()"/>
			</form>
			<script type="text/javascript">
			var worker;
		
			function startFind() {
				var script = document.getElementById('worker').textContent;
				var blob = new Blob([script]);
				
				worker = new Worker(window.URL.createObjectURL(blob));
				worker.onmessage = function(event) {
					document.getElementById('prime').value = event.data;
				}
				worker.postMessage('Start');
			}
		
			function stopFind() {
				worker.terminate();
			}
			</script>
		</body>
	</html>
	

Pass data with transferrable objects

When passing arguments to/from the Web worker thread, the data are copied, which takes some time. Firefox 18 and Chrome 17 support passing data with transferrable objects to save the overhead of copying, which is similar to passing by reference in C++. However, the ownership of the transferrable objects is also transferred from one thread to another thread. Thus, the transferrable object is not available any more in the original thread's context.

Below are the two examples: the first one uses transferrable objects, the second one passes a regular array object to the Web worker thread:

	<!DOCTYPE html>
	<html>
		<head>
			<title>Find Sum</title>
		</head>
		<body>
			<script id="worker" type="text/js-worker">
				self.onmessage = function(event) {
					var array = new Uint8Array(event.data);
					var sum = 0;
					for (var i = 0; i < array.length; i++)
						sum += array[i];
					postMessage(sum);
				}			
			</script>
			<form>
				<label for="prime">The sum: </label><input type="text" id="sum" disabled/> 
				<input type="button" value="SUM" onclick="sumUp()"/>
			</form>
			<script type="text/javascript">
			function sumUp() {
				var script = document.getElementById('worker').textContent;
				var blob = new Blob([script], { type: "text/javascript"});
				
				var array = new Uint8Array(1024 * 1024 * 32); // 32 MB
				for (var i = 0; i < array.length; i++)
					array[i] = 1;
				var worker = new Worker(window.URL.createObjectURL(blob));
				worker.onmessage = function(event) {
					document.getElementById('sum').value = event.data;
				}
				worker.postMessage(array.buffer, [array.buffer]);
				alert(array.length); // shows 0
			}
		
			function stopFind() {
				worker.terminate();
			}
			</script>
		</body>
	</html>
		  
	<!DOCTYPE html>
	<html>
		<head>
			<title>Find Sum</title>
		</head>
		<body>
			<script id="worker" type="text/js-worker">
				self.onmessage = function(event) {
					//var array = new Uint8Array(event.data);
					var array = event.data;
					var sum = 0;
					for (var i = 0; i < array.length; i++)
						sum += array[i];
					postMessage(sum);
				}			
			</script>
			<form>
				<label for="prime">The sum: </label><input type="text" id="sum" disabled/> 
				<input type="button" value="SUM" onclick="sumUp()"/>
			</form>
			<script type="text/javascript">
			function sumUp() {
				var script = document.getElementById('worker').textContent;
				var blob = new Blob([script], { type: "text/javascript"});
				
				var array = new Array(1024 * 1024 * 16); // 16 MB. If I use 32 MB, the program will abort.
				for (var i = 0; i < array.length; i++)
					array[i] = 1;
				var worker = new Worker(window.URL.createObjectURL(blob));
				worker.onmessage = function(event) {
					document.getElementById('sum').value = event.data;
				}
				worker.postMessage(array);
				alert(array.length); //shows 16777216
			}
		
			function stopFind() {
				worker.terminate();
			}
			</script>
		</body>
	</html>
		  

Web Worker reference

WorkerGlobalScope interface

Attributes:

Methods:

Event handler:

DedicatedWorkerGlobalScope interface

Methods:

Event handler:

SharedWorkerGlobalScope interface

Attributes:

Event handler:

Worker interface

Methods:

Event handler:

SharedWorker interface

Attributes:

Methods:

References

  1. HTML Living Standard
  2. Using web workers
  3. Ido Green, Web Workers: Multithreaded Programs in JavaScript