Friday, November 19, 2010

JavaScript Threads

Most web developers must be aware (hopefully), that javascripts are executed on a single threaded environment within browser frame or tab process. At render time your scripts are executed sequentially and have to wait or yield until the previous request is complete. Many might have even noticed browser dialog

To mimic concurrency, developers use techniques like setTimeout(), setInterval(), XMLHttpRequest, and event handlers. All of these features run asynchronously, but non-blocking doesn't necessarily mean concurrency. Asynchronous events are processed after the current executing script has yielded. This is the primary reason why some page onload or javascript controlled behaviors for page rendering work most of the times but not always. Often these issues are blamed to slow network because you may not notice such behaviors every day.

The good news is HTML5 has put an end to this bringing threading into javascript world. Using Web workers, developers can now run heavy weight scripts in their own threads without blocking other components in the page. W3C specifications talks about two types of workers, shared and dedicated.

Dedicated Web Workers run in an isolated thread. As a result, the code that they execute needs to be contained in a separate file. But before we do that, the first thing to do is create a new Worker object in your main page. The constructor takes the name of the worker script:
var worker = new Worker('task.js');
If the specified file exists, the browser will spawn a new worker thread, which is downloaded asynchronously. The worker will not begin until the file has completely downloaded and executed. If the path to your worker returns an 404, the worker will fail silently. As with java threading, javascript threads do not return values or allow you to trap failures.

After instantiating the worker, you start it by calling the postMessage() method:
worker.postMessage(); // Start the worker.

For example, if you have requirement to prefetch some report based on user preference OR notify user when requested report is ready for retireval, you may script something like this on the main page,
...
<page onload="prepReport()"/>
<output id="result"/>
<script>
  function prepReport() {
    worker.postMessage({'cmd': 'submitReq', 'msg': reportParams});
  }
  var worker = new Worker('doRptWork.js');

  worker.addEventListener('message', function(e) {
    document.getElementById('result').textContent = e.data;
  }, false);
</script>
...
...
 
doRptWork.js:
self.addEventListener('message', function(e) {
  var data = e.data;
  switch (data.cmd) {
    case 'submitReq':
      self.postMessage('WORKER STARTED TO PROCESS REPORT REQ: ' + data.msg);
            .... Post an HTTP Request to the server ........

   break;    
     default: self.postMessage('Unknown command: ' + data.msg); };}, false);

Due to their multi-threaded behavior, web workers only has access to a subset of JavaScript's features:
  • The navigator object
  • The location object (read-only)
  • XMLHttpRequest
  • setTimeout()/clearTimeout() and setInterval()/clearInterval()
  • The Application Cache
  • Importing external scripts using the importScripts() method
  • Spawning other web workers
Workers do NOT have access to:
  • The DOM (it's not thread-safe)
  • The window object
  • The document object
  • The parent object  
To check if your browser supports these features, visit http://www.findmebyip.com/ that will list what your browser does and does not support.

No comments:

Post a Comment