Introduction to HTML5 Web Worker

Introduction

 
Web Workers are a part of the HTML5 specification - they enable the creation of background JavaScript threads for CPU intensive tasks, without blocking the UI or any scripts that handle user interaction.
 
This acts as a multi-threading feature and provides parallel execution.
 

Support Detection

 
The first step is to check if the browser supports the Web Worker API. If the browser support is present, there will be a Worker property on the global window object. If your browser doesn't support the Web Worker API, the Worker property will be undefined. Note that the Worker object has the W in upper case!
 

Parallel Job

 
The next step is to create the "Worker" script that is going to perform the CPU intensive tasks we want to run in a parallel thread. This script is saved in a separate .js file.
 

Manage the flow

 
The communication is initiated by passing relevant information in a message from the main script to the worker script using the postMessage method. The worker script responds to the "onmessage" event by fetching the required information from the event and performing the requested task. When the task has completed, the worker posts a message back to the main thread. This message can be a simple variable or a JSON object. The main thread reacts in a similar fashion by handling the onmessage event which was raised by the worker and using the output result as needed. During the time that the worker script was executing, user interaction and other tasks are not blocked and the user is able to interact with the application.
 
You also have the capability to terminate a worker from the main thread, if needed.
 
Do note that web workers only have access to a subset of JavaScript's features to support the concurrent execution.
 

Error handling

 
Of course, we never have any errors in our code ;-) But we do have to make provisions. If an error occurs while a worker is executing, the ErrorEvent is fired.
 

Sample

 
We will try a simple sample now, to take a look at the reality of the implementation. This article is admittedly contrived to provide a working scenario. You can extend the concepts to real-world scenarios involving CPU intensive operations.
 
In the sample, we have a slow process - the code implements a "do nothing" loop (which is purely to demonstrate the concept in this article) and after a delay, returns the user input and the time that the function completes execution.
 
We have a fast process - the code just returns the user input and the execution timestamp.
 
When I tried to run the sample, I entered a string in the userName "slow". Clicked on the slow button which led the delay loop to execute. I could, in the meantime, interact with the application, update the string in the input text box and click the second button. The second button also spawned a worker which returned almost instantly, displaying the updated input and the time.
 
At no point did the application block, pending for any of the threads to return.
  
Take a look at the code now:
 
Code: Worker JavaScript - named as myWorker.js
  1. /* myWorker.js */   
  2. //only for demo purpose   
  3. function ReallySlowCode(millis)  
  4. {  
  5.  var date = new Date();  
  6.  var curDate =  
  7.   null;  
  8.  do {  
  9.   curDate = new Date();  
  10.  }  
  11.  while (curDate - date < millis);  
  12. }  
  13. function slowfunc(userName) {  
  14.  ReallySlowCode(5000);  
  15.  return userName +  
  16.   "-" + new Date();  
  17. }  
  18. function quickfunc(userName) {  
  19.  return  
  20.  userName + "-" + new Date();  
  21. }  
  22. /* The onmessage event listener  
  23. responds to the event raised from the main script, invokes the appropriate  
  24. function based on the data passed. 
  25. */  
  26. this.onmessage = function(event) {  
  27.   var data = event.data;  
  28.   switch (data.op) {  
  29.    case 'slow':  
  30.     postMessage(slowfunc(data.userName));  
  31.     break;  
  32.    case 'quick':  
  33.     postMessage(quickfunc(data.userName));  
  34.     break;  
  35.    default:  
  36.     postMessage("Wrong   
  37.      operation specified ");  
  38.     }  
  39.   };  
Main: named as testww.html
  1. <!DOCTYPE html>    
  2. <body>    
  3.     <table border="0">    
  4.         <tr>    
  5.             <td>Your     
  6. Name</td>    
  7.             <td>    
  8.                 <input type="text" id="userName"     
  9. />    
  10.             </td>    
  11.         </tr>    
  12.         <tr>    
  13.             <td>Slow     
  14. Process</td>    
  15.             <td>    
  16.                 <input type="text" id="slowResult"     
  17. size="100"/>    
  18.             </td>    
  19.         </tr>    
  20.         <tr>    
  21.             <td>Quick     
  22. Process</td>    
  23.             <td>    
  24.                 <input type="text" id="quickResult"     
  25. size="100"/>    
  26.             </td>    
  27.         </tr>    
  28.         <tr>    
  29.             <td     
  30. colwidth="2">    
  31.                 <input type="button" id="slowButton" value="Run Long     
  32. process" />    
  33.                 <input type="button" id="quickButton" value="Run quick     
  34. process" />    
  35.             </td>    
  36.         </tr>    
  37.     </table>    
  38.     <script>    
  39. /* Check if Web Workers are supported   
  40. */    
  41. function getWebWorkerSupport() {    
  42. return (typeof(Worker) !==     
  43. "undefined") ? true:false;    
  44. }    
  45. if(getWebWorkerSupport()     
  46. == true)    
  47. {    
  48. var userName,y,message;    
  49. /* Create the new workers.   
  50. each instance runs in parallel */    
  51. slowWorker = new     
  52. Worker("myWorker.js");    
  53. quickWorker = new     
  54. Worker("myWorker.js");    
  55. /* Bind an event listener for the onmessage   
  56. function for each of the workers. this will be invoked by the worker thread when   
  57. the execution has completed.  
  58. */    
  59. slowWorker.onmessage = function (event)     
  60. {    
  61. document.getElementById("slowResult").value =     
  62. event.data;    
  63. };    
  64. quickWorker.onmessage = function (event)     
  65. {    
  66. document.getElementById("quickResult").value = event.data;    
  67. };    
  68. /* Register events for buttons   
  69. */    
  70. document.getElementById("slowButton").onclick = function() {    
  71. userName = document.getElementById("userName").value;    
  72. message = {    
  73. 'op'     
  74. 'slow',    
  75. 'userName'  :     
  76. userName    
  77. };    
  78. slowWorker.postMessage(message);    
  79. }    
  80. document.getElementById("quickButton").onclick     
  81. function() {    
  82. userName = document.getElementById("userName").value;    
  83. message = {    
  84. 'op'     
  85. 'quick',    
  86. 'userName'  :     
  87. userName    
  88. };    
  89. quickWorker.postMessage(message);    
  90. }    
  91. }    
  92. </script>    
  93. </body>undefined</html>     
Sample in Action - open testww.html in a compliant browser (the following results are on Firefox 5)
 
webworker1.jpg 
 

Conclusion

 
Web Workers utilize a thread-like message passing mechanism to achieve parallelism. They keep the UI performant and responsive to users. No more "A script on this page is busy or has stopped responding..." errors!!
 
Happy Coding!