Friday, September 21, 2012

Importance of non-blocking calls in JavaScript

In javascript there is only one thread. The same thread is responsible for running your code as well as updating the UI. Think about a server call which takes around 5 sec to come back. If we make such server call and wait for its response then the UI will be stuck for 5 sec. In order to avoid such circumstances we generally use callback methods. lets take the following example -

         function Greeter(s){
               this.salutation = s;
         }
         function Server(){}
              Server.prototype.getUser = function(fn){
              // Make a service call and fetch the user details
              fn.apply(this,['user']);
        }
        Greeter.prototype.greet = function(name){
            alert(this.salutation + '-' + name);
        }
        var greeter = new Greeter('Salutation');
        var server = new Server();

        server.getUser(greeter.greet); // undefined - User ----1
        server.getUser(
                function(name){
                    greeter.greet(name); // Salutation - User ----2
                }
        );
In the above example we are making a server call and passing a callback method, once the call is complete we want to greet the user. With the 1st attempt of passing the call back function it alerts 'undefined - User', that is because the 'this' got changes. In this 1st case once we call greet, it gets call on window not on greet, thus it try to get this.salutation(which becomes window.salutation) and returns undefined.  It happens because we are not calling the function in this line -  server.getUser(greeter.greet); there is no parenthesis here, we are just passing a variable name here, which is pointing at a function. The actual call is made from fn.apply (inside Server.getUser), where this becomes window and thus gives unexpected result.
In the 2nd case we are passing a function which eventually calls  greeter.greet() function, here this object is greeter which is never lost because we are actually calling it here. and thus we get expected result.

The above code is not very much scalable, Which means we will need to do the same thing for every callback function. We can make this code more generic like this -

function bind(funcThis,fn){
        return function(){
            return fn.apply(funcThis,arguments);
        }
    }
and then use this to bind greeter with greet -server.getUser(bind(greeter,greeter.greet));

Browser has a queue of events/tasks that needs to be done ordered by timestamp. If we have a call to server and an associated call back function with it, then the callback event/function actually get en-queued into the browser queue at the head of the queue. In other words the callback function takes the priority in the queue.
Here is the pseudo code which shows how browser behaves -
while(true){
   var event = waitForEvent();
   try{
        event();
   }catch(e){}
   redrawDOM();
}

Browser keeps waiting until next event comes and once it comes, it executes it.

No comments:

Post a Comment