8

Since using $.Deferred I've run into this scenario a couple times: I have a list of values each of which yields a Deferred Object in some way and I want to execute a callback once all of the Deferred Objects are resolved.

A more concrete example would be something like this:

var urls = [ 'foo.com', 'bar.com', 'baz.com', 'qux.com' ],
    defers = [], defer;

for( var i = 0, j = urls.length; i < j; i++ ){
    defer = $.ajax({
        url: 'http://' + urls[ i ]
    });

    defers.push(defer);
}

$.when.apply(window, defers).done(function(){
    // Do Something
});

Is there a more elegant solution than the code in my example?

2
  • 2
    Why do you think, that this is not elegant? Commented Nov 3, 2011 at 22:05
  • After writing code like this for the 3rd time I began to think it's a fairly common scenario and might be handled by the Deferred Objects framework in a better way that I was simply overlooking. Commented Nov 4, 2011 at 12:25

3 Answers 3

3

Yes there is, you should never reference a lookup value in a loop. Always make a copy.

var urls = [ 'foo.com', 'bar.com', 'baz.com', 'qux.com' ],
    defers = [], defer;

var urlsLength = urls.length;
for( var i = 0, j = urlsLength; i < j; i++ ){
    defer = $.ajax({
        url: 'http://' + urls[ i ]
    });

    defers.push(defer);
}

$.when.apply(window, defers).done(function(){
    // Do Something
});

But seriously, I'm just joshin' you. That code rocks. Stick with it.

Sign up to request clarification or add additional context in comments.

1 Comment

You are correct about the lookup value but having it in the declaration (i.e. var i = 0, j = urls.length) caches it. What you want to avoid is having it in the comparison (i.e. i < urls.length). :-)
3

A more elegant way to write this example is with the array map function (or jQuery's $.map):

var urls = [ 'foo.com', 'bar.com', 'baz.com', 'qux.com' ];

var defers = urls.map( function( url) {
    return $.ajax({
        url: 'http://' + url
    });
});

$.when.apply(window, defers).done(function(){
    // Do Something
});

You could even roll your own "whenDone" and "fetchURL" functions:

Array.prototype.whenDone = function(callback){
    return $.when.apply(window, this).done(callback);
}

function fetchURL(url){
    return $.ajax({
        url: 'http://' + url
    });
}

var urls = [ 'foo.com', 'bar.com', 'baz.com', 'qux.com' ];    

urls.map( fetchUrl ).whenDone(function(){
    // Do Something
});

Comments

0

Here is a helper function I wrote called LoadInitialData, it can be called like this LoadInitialData(urlArray, dataReturnedArray, callback)

    ///  
///  1. The magical function LoadInitialData
///  

            ///  
            /// <summary>
            ///    This functions allows you to fire off a bunch of ajax GET requests and run a callback function when
            ///    all the requests come back that contains an array of all your ajax success data
            /// </summary>
            /// <params>
            ///           urlArray - an array of urls to be looped and ajaxed
            ///    dataReturnedArray - this array will contain all data returned from your ajax calls. Its stuctured like this
            ///         [{url: "http//site.com/1", "data": "your data"}, {url: "http//site.com/2", "data": "your data"}]
            ///           dataReturnedArray[0] is data from call 1,  dataReturnedArray[1] is data from call 2 etc. It might be a 
            ///         good idea to pass in a global array so you can use this data throughout your application.
            ///     callback - a function that runs after all ajax calles are done, dataReturnedArray is available in the callback
            /// </parms>
            ///
            function LoadInitialData(urlArray, dataReturnedArray, callback){
               // set up a deffered promise to fire when all our async calls come back
                var urls = urlArray, defers = [], defer;
                   var urlsLength = urls.length;
                   for( var i = 0, j = urlsLength; i < j; i++ ){
                       var u = urls[ i ];
                          defer = $.ajax({
                          type : "GET",
                           dataType : "jsonp",
                           url: u,
                           success: function(data){
                                 dataReturnedArray.push({
                                        url: u,
                                        data: data
                                 });
                           }
                       });
                       defers.push(defer);
                   }
                   $.when.apply(window, defers).then(function(){   
                          // Do Something now that we have all the data
                       console.log("done fetching all data");
                       callback(dataReturnedArray);
                   });
             }



///  
///  2. Your config…. urlArray, dataReturnedArray, callback
///  

         var app = app || {};
         app.data = []; // will hold the fetched data
         var urlArr = ["http://site.com/2", "http://site.com/2"]; // the urls to get data from


         // function to call once all the data is loaded
         callback = function(data){

            // data cleansing
            var tblData = [];                        
            $.each(data, function(key, value){
                   $.each(value.data, function(key, value){
                          tblData.push(value); 
                   });
             });

            $("#loader").hide();
         };


///  
///  3. Kick it all off!
///  

         // show a loader here
         $("#loader").show();

         // fire off the code to fetch the initial data
         LoadInitialData(urlArr, app.data, callback);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.