Voice of a Developer: JavaScript Web Workers - Part 21

Introduction

 
JavaScript is the language of the web. This series of articles will talk about my observations learned during my decade of software development experience with JavaScript.
 
Before moving further let us look at the previous articles of the series:
In the last article, we learned about ECMAScript 6 and now we will understand some features which developers can use to process complex scripts in the background via Web Workers. 
 

Web Worker

 
A web worker is a JavaScript running in the background, without affecting the performance of the page. They are generally external script files that run in the background. It is supported by majority of browsers.
 
 

Advantages

 
We know web browsers increased a lot over the past few years and it is primarily because of a lot of work done on its engines, ex- V8 (Google), Chakra (Microsoft). The JavaScript so far runs in a single thread. The problem with single-threaded architecture is that it blocks the code and UI becomes unresponsive in case of running the complex script. There are various ways to solve this problem:
  • Offload work to server, but to make apps faster fat client is preferred
  • Use asynchronous calls, but many complex ecysystem of async calls & promises could lead into callback hell
  • Leverage multi threading. Interesting!
Web Workers solve this issue by providing capability of multi threading in JavaScript.  
 
Let's define a problem first and solve for that.
 

Problem statement

 
If you run a heavy JavaScript script then the UI becomes unresponsive and throws the following error. This is a very common problem “Unresponsive script” that we all have seen some day or the other.
 
Unresponsive script error
 
I've written sample code with a problem and the solution also.
 
Index.html
  1. <!DOCTYPE html>    
  2. <html>    
  3. <head>    
  4.     <title>Unresponsive dialog problem</title>    
  5. </head>    
  6. <body>    
  7. <input id="num">    
  8. <button id="btnWordsSol">Get Words - Solution</button>    
  9. <button id="btnWordsPrb">Get Words - Problem</button>    
  10. <button id="btnTerminate">Terminate</button>    
  11. <button id="btnClear">Clear</button><br><br>    
  12. <div id="msg"></div>    
  13. <script type="text/javascript" src="js/vendor/jquery-1.10.2.min.js"></script>    
  14. <script type="text/javascript">    
  15.     var worker=new Worker('js/solutions.js');    
  16.     $('#btnClear').click(function() {    
  17.         $('#msg').text('');    
  18.     });    
  19.     
  20.     $('#btnTerminate').click(function() {    
  21.         $('#msg').text('');    
  22.         if(worker)    
  23.         {    
  24.             worker.terminate();    
  25.             worker=null;    
  26.             $('#msg').text('worker destroyed');    
  27.             $('#prg').val(0)        ;    
  28.         }    
  29.     });    
  30.     $('#btnWordsSol').click(function() {    
  31.         $('#msg').text('');    
  32.     
  33.         var num=$('#num').val();    
  34.         if(worker == null)    
  35.             worker=new Worker('solution.js');    
  36.         worker.postMessage(num);    
  37.         worker.onmessage=function(event) {    
  38.             if(event.data.percent!=null)    
  39.                 $('#prg').val(event.data.percent)    
  40.             $('#msg').text(event.data.showText);    
  41.         }    
  42.     
  43.         worker.onerror=function(event) {    
  44.             $('#msg').text(event.lineno+ ' : '+ event.message);    
  45.         }    
  46.     });    
  47.     
  48.     $('#btnWordsPrb').click(function() {    
  49.         $('#msg').text('');    
  50.     
  51.         var text1="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore t dolore magna aliqua. Ut enim ad minim veniam,quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodoconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat nonproident, sunt in culpa qui officia deserunt mollit anim id est laborum.";    
  52.         var arrOfWords=text1.split (" ");    
  53.         // console.log('length of our para: '+arrOfWords.length)    ;    
  54.         var tempStr="";    
  55.         var index;    
  56.         var num=$('#num').val();    
  57.         for (var i = 1; i <= num; i++) {    
  58.             index= Math.floor(Math.random()*arrOfWords.length);    
  59.             tempStr+=" "+arrOfWords[index];    
  60.         }    
  61.         $('#msg').text(tempStr);    
  62.     
  63.     });    
  64. </script>    
  65.     
  66. </body>    
  67. </html>   
    Download this code and run this in the webserver, for example http://localhost/webworker/index.html.
     
    It looks like this:
     
    Unresponsive dilog problem1
     
    Enter any number, say 100 in the text box and click “Get Words – Problem” and see the output of random words, for example:
     
    Unresponsive dilog problem2
     
    This button will work if you want to generate the number of words in the text box, say 1000000 (it may be less at your machine). But if you enter a higher value, say 10000000, then after clicking “Get Words – Problem” it'll throw the following message after some time:
     
    warning Unresponsive script
     

    Solution

     
    To solve these problems, let's leverage the WW provided by HTML5. Enter the same number in the text box 10000000 and click “Get Words – Solution” and it'll work. We'll review Web Worker life cycle, i.e.,
    • spawning a worker (installing, activate)
    • idle
    • post message
    • terminate
    • error
       
     

    Spawning a worker

     
    To create a Web Worker is simple and you need a new JavaScript file that contains code that you want WebWorker to execute. Hence, the below code will create a new object from Worker.
    1. $('#btnWordsSol').click(function()    
    2.     
    3. var worker=new Worker('solution.js');   

    Communicating with a Web Worker

     
    To use these Web Workers in real world you need to establish a communication. These messages could be simple string, objects. The preceding line will load the script located at “worker.js” and execute it in the background. You need to call the Worker() constructor with the URI of a script to execute in the Worker thread as in the following:
    1. worker.postMessage(num);    
    2.     
    3.  worker.onmessage=function(event) {    
    4.    if(event.data.percent!=null)    
    5.     $('#prg').val(event.data.percent)    
    6.    $('#msg').text(event.data.showText);    
    7.   }   
      If you want to get data from the Worker (for example, the output of the processed information, notifications, and so on) then you should set the Worker's onmessage property to an appropriate event handler function. The onmessage event is a callback that receives the value from the background JavaScript. It also sends data back via postMessage.
       

      Capturing error

       
      Web Workers support onerror event. The onerror event captures a JavaScript error that occurs in the background JavaScript.
      1. worker.onerror=function(event) {    
      2.   $('#msg').text(event.lineno+ ' : '+ event.message);    
      3.  }   
      If you want to terminate WW in between then you can click the Terminate button that will kill between the worker processes as in the following:
       
      worker.terminate(); 
       
      terminate WW
       
      You can use self or this keyword to access onmessage. It's sending data back to problem.html via self.postMessage.
       
      So, communication between the foreground thread running problem.html and the background thread running solution.js is done via postMessage.
       

      Theory of WW

       
      WW runs JavaScript via background thread. Don't be surprised, JavaScript now has background thread execution capability. We know JavaScript is single-threaded and scripts execute sequentially.
       
      But, it's a real thread, spawned by the OS, that executes in the background.
       

      Features available to WW

       
      Due to the multi-threaded behavior, a WW only has access to the following subset of JavaScript features:
      • the navigator object
      • the location object
      • XMLHttpRequest
      • setTimeOut/clearTimeOut/setInterval/clearInterval
      • importScripts
      • spawning other WW

      Limitations of WW

       
      It has some limitations apart from its multithreading advantage, i.e., it can't access:
      • DOM elements
      • window object
      • document object
      • parent object

      Types of WW

       
      Primarily there are three dedicated, shared, and inline workers. What we've seen above is an example of dedicated. I'll post another blog for shared and inline in detail and some more interesting features. Keep watching this space. 
       
      Please share the feedback.