Thursday, May 30, 2013

Who said there is no thread in JavaScript?...

JavaScript is supposed to show up as single-threaded. Is quite possible to mimic thread's behavior though, which can be pretty useful for example to deal with tasks you want to abort if they take too long to complete. In Java, this is quite natural. It is also feasible in JavaScript. Look at the code below:
  var i = 0;
  var max   = 10;   // Nb loops
  var delay = 1000; // ms between loops
  var completed = false;
  var workerID, watcherID;

  var performLoop = function(clientChannel)
  {
    console.log('... Loop # ' + i);
    if (++i < max)
    {
      workerID = setTimeout(function()
      {
        performLoop(clientChannel);
      }, delay);  // There is a wait here, just to waste time.
    }
    else
    {
      completed = true;
      clientChannel("End of loop, clearing the watcher.");
      clearTimeout(watcherID);
    }
  };
  
  var workAndWatch = function(worker, timeout, cb)
  {
    console.log("Starting Worker");
    // The worker
    setTimeout(function() 
    {
      worker(cb);
    }, 0);
    console.log("Starting Watcher");
    // The watcher
    watcherID = setTimeout(function() 
    {
      if (!completed)
      {
        cb("Killing the lazy worker.");
        clearTimeout(workerID);
      }
    }, timeout);
    console.log("Watcher & worker started.");
  };
  var callback = function(mess)
  {
    console.log(mess);
  };
  workAndWatch(performLoop, 10000, callback);
    
The work to do is describe in the function performLoop.
What we want here, is to make sure whatever runs will not take more than a given amount of time.
This amount of time is given as the second parameter of the function workAndWatch.
  workAndWatch(performLoop, 5000, callback);
I use node.js to run this script.
Let's give a 5 seconds timeout:
  Prompt> node hang.detection.js
  Starting Worker
  Starting Watcher
  Watcher & worker started.
  ... Loop # 0
  ... Loop # 1
  ... Loop # 2
  ... Loop # 3
  ... Loop # 4
  Killing the lazy worker.
And now, let's give it 10 seconds:
  workAndWatch(performLoop, 10000, callback);
  Prompt> node hang.detection.js
  Starting Worker
  Starting Watcher
  Watcher & worker started.
  ... Loop # 0
  ... Loop # 1
  ... Loop # 2
  ... Loop # 3
  ... Loop # 4 
  ... Loop # 5
  ... Loop # 6
  ... Loop # 7
  ... Loop # 8
  ... Loop # 9
  End of loop, clearing the watcher.
It actually behaves like if we were dealing with two threads. The cool thing is that we don't need to worry about any kind of synchronization... ;0)