Home > JavaScript Kung-Fu > Optimizing Asynchrony With JavaScript

Optimizing Asynchrony With JavaScript

July 7th, 2011 admin

I was recently faced with a problem where I needed a bunch of loosely related asynchronous events to occur before I proceeded with some other javascript execution. None of these asynchronous tasks were dependent on the response from one another, but I need them ALL to be complete before I could progress.

In other words:
I needed A,B,C, and D to perform E…
I did not need A to do B to do C to do D.

Originally I saw myself doing something stupid (and confusing) with the implementation. I was executing each asynchronous operation as part of a serial cascade… something like:

$(window).load( function()
{
    $.getScript( 'http://cdn.simplegeo.com/js/1.3/simplegeo.all.min.js', function(){
         $.ajax({
           'url': 'http://twitter.com/statuses/user_timeline/MW_Collins.json',
           'data': { count: 5 },
           'success': function(){ /* do something */ },
           'dataType': 'jsonp'
         });
    });
});

This wasn’t my original probem… I’m not sure what nonsense you’d be up to where you would need simplegeo right before you load a twitter feed, kind of a weird example, but just bear with me for the concept.

The problem here is that each of these events ends up loading in serial succession because each asynchronous event occurs on completion of the last. But none of these operations was dependent on the last, they all could have happened in parallel.

So instead I could have done something like:

$(window).load( function()
{
...
});

$.getScript( 'http://cdn.simplegeo.com/js/1.3/simplegeo.all.min.js', function(){
...
});

$.ajax({
       'url': 'http://twitter.com/statuses/user_timeline/MW_Collins.json',
       'data': { count: 5 },
       'success': function(){ /* do something */ },
       'dataType': 'jsonp'
});

So these are in parallel… Oh but wait, the problem here is that I need to be sure window.load has run AND simplegeo has loaded AND twitter has returned BEFORE I do something with the twitter feed. Hmmm….

function satisfy(actions,callback)
{
  var alen = actions.length;
  var ac = 0;
  var sf = {
    done: function(){
      if (++ac === alen ){
        callback();
      }
    }
  };

  $(actions).each( function(){
    this(sf);
  });
}

var actions = [
  function(satisfier){
    $(window).load( function(){
      console.log('Window Load Done');
      satisfier.done();
    });
  },

  function(satisfier)
  {
    $.getScript( 'http://cdn.simplegeo.com/js/1.3/simplegeo.all.min.js', function(){
      console.log('Get Script Done');
      satisfier.done();
    });
  },

  function(satisfier){
    $.ajax({
      'url': 'http://twitter.com/statuses/user_timeline/MW_Collins.json',
      'data': { count: 5 },
      'success': function(){
        console.log('Twitter Done');
        satisfier.done();
      },
      'dataType': 'jsonp'
    });
  }
];

satisfy( actions, function(){
  console.log('Everything is done...');
  // Now I can do something stupid with Twitter and Simple Geo
});

Ahhh, yes, now this does what I want. I’ve created a method called “satisfy” that can take a list of action methods. Each action method takes a “satisfier” parameter and once each asynchronous operation is complete, it calls done(). Once all the methods are complete, the final callback method is triggered and I can proceed with my stupid mashup.

Now think about what you could do with satisfy handlers that make other calls to satisfy… sounds like a fun way to perform massively parallel rendering with NodeJS for example. Hmmm….

Categories: JavaScript Kung-Fu Tags:
Comments are closed.