0

I need to fetch data (Q & A) from a distant service using their API. Data is split in different categories and the only methods they offer allow me to list categories and items from a specific category. However, My briefing implies that I gather data from different categories. I ended up doing this (this will gather data from all categories):

var getEveryQA = function(sLang)
{
    var allQA = [];
    //This request is for getting category listing
    $.ajax({
            crossDomain: true,
            contentType: "application/json; charset=utf-8",
            url: category_list_URL,
            data: { Lang: sLang }, 
            dataType: "jsonp",
            success: function(responseData){
                for (var i = 0; i < responseData.length; i++) 
                {
                    if(responseData[i].Code.toLowerCase !== "all")//category "all" has no real existence although it is returned in categories listing
                    {
                        //Request items for each category
                        $.ajax({
                            crossDomain: true,
                            contentType: "application/json; charset=utf-8",
                            url: items_by_category_URL,
                            data: { Lang: sLang, Category: responseData[i].Code }, 
                            dataType: "jsonp",
                            success: function(responseData){
                                    allQA = allQA.concat(responseData);//object from this response will be concatenated to the global object
                                }
                        });

                    }
                }
            }
        }); 
}

What I would like is to trigger a sorting method whenever all the AJAX calls done in my for loop have succeeded. I've the feeling jQuery's deferred is the solution, but the many examples I've read weren't compatible with my for... loop structure. Is there a way to build some kind of "queue" out of my multiple callbacks that I could then pass as an argument to the deferred method ? Or maybe am I looking in the wrong direction ?

18
  • 1
    Use $.when. Place your ajax calls in an array, then call $.when(thatarray).then(function(){Do something with all the results}); You can add the results in another array (as the come back in any order) ans sort them in the then function. Commented Sep 22, 2014 at 14:07
  • @TrueBlueAussie keep in mind, $.when doesn't work with an array unless you also use .apply Commented Sep 22, 2014 at 14:24
  • @KevinB Try at console , this page : var q = []; q.push($.ajax(), $.ajax()); $.when(q).done(function(data) {console.log(data)}) // [Object, Object] . fwiw, alternatively , var dfd = new $.Deferred(); dfd.resolveWith($, [$.ajax(), $.ajax()]); dfd.done(function(data) { console.log(data, data)}) Commented Sep 22, 2014 at 14:48
  • @guest271314 $.when, when given a non-promise, will resolve immediately. Commented Sep 22, 2014 at 14:49
  • @guest271314 Here's an example: jsfiddle.net/17a1uhms/2 Notice how console.log 3 happens immediately rather than after 2 seconds, and how console.log 4 happens 2 seconds later due to proper use of $.when with .apply Commented Sep 22, 2014 at 14:55

2 Answers 2

3

Edit

That would have been a good (and simpler) approach but I don't think it will work. allQA will consist of all the QA of all categories, and therefore the sort would occur after the first iteration (when all we have pushed into that array are the first results) while I need it to occur when allQA is "full" –Bartdude

Note $.ajax() call's response variable in for loop : _responseData (see leading _ underscore) - different variable than the initial $.ajax() response responseData (no leading underscore) .

The sort , or, other tasks, should only occur once allQA.length === responseData.length -- not _responseData.length (which is individual $.ajax() responses concatenated within for loop).

Adding , including additional deferred or promise pieces not needed to accomplish task described at original Question -- unless Answer specifically require solving utilizing deferred or promise methods.

length properties of objects or arrays involved should be sufficient to accomplish task described at OP.

Example piece at jsfiddle http://jsfiddle.net/guest271314/sccf49vr/ , utilizing same pattern as originally posted :

var arr = [7, 5, 6, 1, 8, 4, 9, 0, 2, 3]; // unsorted array
$("body").append(arr + "<br />");
var getEveryQA = function (sLang) {
    var allQA = [];
    //This request is for getting category listing
    $.ajax({
        type: "POST",
        contentType: "application/json; charset=utf-8",
        url: "/echo/json/",
        data: {
            json: JSON.stringify(sLang)
        },
        dataType: "json",
        success: function (responseData) {
            for (var i = 0; i < responseData.length; i++) {
                // category "all" has no real existence although 
                // it is returned in categories listing
                if (responseData[i] !== "all") {
                    //Request items for each category
                    $.ajax({
                        type: "POST",
                        contentType: "application/json; charset=utf-8",
                        url: "/echo/json/",
                        data: {
                            json: JSON.stringify(responseData[i])
                        },
                        dataType: "json",
                        // note leading `_` underscore at `for` loop
                        // `_responseData` -- different than initial 
                        // `responseData` response from first request
                        success: function (_responseData) {
                            //object from this response will be concatenated to the global object
                            allQA = allQA.concat(_responseData);
                            // do stuff when `allQA` length === `responseData` (for loop) length
                            console.log(allQA.length === responseData.length);
                            if (allQA.length === responseData.length) {
                                // do sorting stuff
                               allQA.sort(); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
                               console.log(allQA, arr);
                               $("body").append(allQA.join(",")); 
                            }
                        }
                    });
                }
            }
        }
    });
};

getEveryQA(arr);

Try

for (var i = 0; i < responseData.length; i++)  {
  // category "all" has no real existence although 
  // it is returned in categories listing
  if(responseData[i].Code.toLowerCase !== "all") {
      //Request items for each category
      $.ajax({
        crossDomain: true,
        contentType: "application/json; charset=utf-8",
        url: items_by_category_URL,
        data: { Lang: sLang, Category: responseData[i].Code }, 
        dataType: "jsonp",
        success: function(_responseData){
          //object from this response will be concatenated to the global object
          allQA = allQA.concat(_responseData);
            // do stuff when `allQA` length === `responseData` (for loop) length
            if (allQA.length === responseData.length) {
              // do sorting stuff
            }
         }
     });    
   }
};
Sign up to request clarification or add additional context in comments.

3 Comments

That would have been a good (and simpler) approach but I don't think it will work. allQA will consist of all the QA of all categories, and therefore the sort would occur after the first iteration (when all we have pushed into that array are the first results) while I need it to occur when allQA is "full"
@Bartdude Note leading _ underscore at $.ajax() calls response in for loop. Includes leading _ underscore , differentiating those variables from initial responseData (no leading _ underscore) object. See updated post , jsfiddle . Thanks
Indeed ! My bad ! That's an upvote then as we can't validate 2 answers and the answer from TrueBlueAussie lead me to learn about ´when()´ and ´apply()´ functions and looks like a more elegant pattern, although more complex to read back from where I see it.
2

From comment: Use $.when. Place your ajax calls in an array, then call $.when.apply(thatarray).then(function(){Do something with all the results});

You add all your results into another array already (as they come back in any order) and sort them etc in the then function:

var getEveryQA = function(sLang)
{
    var allQA = [];
    //This request is for getting category listing
    $.ajax({
            crossDomain: true,
            contentType: "application/json; charset=utf-8",
            url: category_list_URL,
            data: { Lang: sLang }, 
            dataType: "jsonp",
            success: function(responseData){
                var requests = [];
                for (var i = 0; i < responseData.length; i++) 
                {
                    if(responseData[i].Code.toLowerCase !== "all")//category "all" has no real existence although it is returned in categories listing
                    {
                        //Request items for each category
                        requests.push($.ajax({
                            crossDomain: true,
                            contentType: "application/json; charset=utf-8",
                            url: items_by_category_URL,
                            data: { Lang: sLang, Category: responseData[i].Code }, 
                            dataType: "jsonp",
                            success: function(responseData){
                                    allQA = allQA.concat(responseData);//object from this response will be concatenated to the global object
                                }
                        }));
                    }
                }
                $.when.apply(requests).then(function(){
                    // Do something with your responses in allQA
                });
            }
        }); 
}

the return value from $.ajax is a promise that can be aggregated with other promises. $.when is about the only method that can take an array of promises and let you do things after all complete (or any fail).

6 Comments

I prefer the .when solution, but.. you haven't used it properly. $.when does not accept an array of promises unless you use .apply to apply the array as multiple arguments to .when.
Technical correction. $.when() doesn't take an array. It takes multiple arguments that are promises (a huge oversight in design in my opinion, but that is the way it is). You would have to use $.when.apply($, array) to pass it an array.
This solution coupled with the use of apply() as suggested by jfriend00 seems to do the trick. I was close to it but still it made me spare quite some time ! I will accept this solution as soon as it will use the apply() method on the last instruction.
Updated. I should have remembered that, but don't use it this way often. Cheers.
Validated ! But please update also the beginning of your answer accordingly so that it's perfectly correct :-) thanks for your help and the learnings.
|

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.