Multi-Threading in javascript using Web workers
As we know javascript is single threaded in nature, So due to this limitation long-running javascript code blocks UI thread(freezes the main window) that handles all the task for drawing, refreshing, animating, user inputs events, etc. So as the page freezes, user is not able to interact with your application any more(very unpleasant user experience) and he/she will probably decide to kill the tab or the browser instance.
To avoid blocking UI thread, browsers have implemented a protection mechanism which alerts users when a long-running suspect script occurs. But this mechanism can’t tell the difference between a script not written correctly and a script that just needs more time to accomplish its work.
Here is an example for long running-script
[javascript]
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script type="text/javascript">
while(true);
</script>
</body>
</html>
[/javascript]
On running the above code your browser will get hang and a warning message will appear.
If we don’t want such warnings and unresponsiveness, we need to run that javascript code in parallel(or in background). To do so, we simply use setTimeout() and setInterval() methods. But they still run in single thread manner. Here is an example.
[javascript]
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p id="p"></p>
<script type="text/javascript">
var i = 0,p = document.getElementById(‘p’);
var myInterval = setInterval(function() {
p.textContent = i++;
if (i > 10) {
clearInterval(myInterval);
}
}, 100);
alert("No writes on document until you closes alert box.");
</script>
</body>
</html>
[/javascript]
As you run the above code in the browser, you can notice that there will be no writes until you dismiss alert box.
For this, HTML5 having a feature for thread like behavior called Web Worker.
Using Web Worker
Web Workers allow you to run JavaScript in parallel on a web page, without blocking the user interface.
- Web workers executes in separate thread
- Need to host all the worker code in separate file
- They aren’t automatically garbage collected, So you need to control them.
- To run worker use worker.postMessage(“<message or json>”);
- To stop worker there are two methods terminate() from caller code and close() from Worker itself
- Instantiating a worker will cost some memory.
Steps to write code to be run by web worker
- Add event listener to the worker itself
[javascript]
this.addEventListener(‘message’, function(event){
// your code to run on worker’s start
}, false);
[/javascript] - To communicate with caller code send message to worker(self) object
[javascript]
this.postMessage("<result to be returned after proceesing>");
[/javascript]
Steps to use Web Workers
- Instantiate Worker for javascript you want to run.
[javascript]
var myWorker = new Worker("<path to worker js file>");
[/javascript] - Start web worker by posting a message by providing inputs as string or json
[javascript]
myWorker.postMessage("This is a input for worker");
[/javascript] - Listen from worker
[javascript]
myWorker.addEventListener("message", function(event) {
var dataFromWorker = event.data;
// Consume result returned from worker
}, false);
[/javascript]
Sample code
helloWorker.js
[javascript]
function messageHandler(event) {
// Accessing to the message data sent by the main page
var count = event.data;
// After doing some processing Posting back the message to the main page
var i = 0;
setInterval(function() {
this.postMessage(i++);
if (i > count) {
this.close();
}
}, 100);
}
[/javascript]
index.html
[javascript]
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p id="p"></p>
<script type="text/javascript">
/* check if browser supports Worker or not */
if (window.Worker) {
// Instantiating the Worker
var myHelloWorker = new Worker(‘helloWorker.js’);
// Getting ready to handle the message sent back by the worker
myHelloWorker.addEventListener("message", function(event) {
document.getElementById(‘p’).textContent = event.data;
}, false);
// Starting the worker by sending a first message (e.g count)
myHelloWorker.postMessage(10);
alert("Changes will be reflected even alert box is opened");
} else {
document.write("Web Workers are not supported by your browser. Try with IE10: <a href=\"http://ie.microsoft.com/testdrive\">download the latest IE10 Platform Preview</a>");
}
</script>
</body>
</html>
[/javascript]
As you run the above code in browser, you will notice that worker code is executes in parallel, As web element text will be changing at the same time when alert box is opened.
Use cases
- Parallel processing
- Sometimes it is needed to execute some code parallely(render ui, getting data in background, record events happening on browser)
- Guaranteed code execution even refresh/back/click happening on current page
- If the user does a page transition during this process (e.g clicks a link), your JavaScript objects from the previous page go away and you can’t process callbacks. When a web worker is used, this activity happens out of band, so you have a better guarantee that it will complete.
- Web I/O
- Poll URLs in background so that you don’t block the UI waiting for polling results.
- Movement Tracking
- The video element, the canvas element, and drawing video frames to a canvas. All of the motion detection it taking place in the background worker.
References
- https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
- https://msdn.microsoft.com/en-us/hh549259.aspx
- http://ejohn.org/blog/web-workers/
- http://www.htmlgoodies.com/html5/tutorials/introducing-html-5-web-workers-bringing-multi-threading-to-javascript.html#fbid=kG1-R7k-FZV
- http://caniuse.com/#feat=webworkers
- http://stackoverflow.com/questions/2773682/what-are-the-use-cases-for-web-workers