0

I have to make a call to the Agile Central API to get a list of defect suites and then iterate through the list and make a nested call to get the list of defects in each suite, the nested call depends on the outer call. I then have to append the rows of data to a table and then call doneCallback() to signal the end of data collection. The problem I'm having is that doneCallback() is being called before the requests have completed so none of the data is actually passed on

I've tried the approaches in this post: Wait until all jQuery Ajax requests are done? and this post: how to wait until Array is filled (asynchronous). In the console I can see that all the data I want is there but nothing gets appended. My question is: how can I make sure I don't call doneCallback() until all the requests that are made in the loop have finished and pushed the data? Here's my code right now:

function getSuites() {
        return $.ajax({
            url: suitesURL("71101309592") + "&fetch=Name,FormattedID,Defects",
            type: "GET",
            xhrFields: {
                withCredentials: true
            },
            headers: {
                "zsessionid": apiKey
            }
        });
    }

    function getDefects(_ref) {
        return $.ajax({
            url: _ref,
            type:"GET",
            xhrFields: {
                withCredentials: true
            },
            headers: {
                "zsessionid": apiKey
            }
        });
    }

    // Download the data
    myConnector.getData = function (table, doneCallback) {

        console.log("Getting Data...");

        var ajaxCalls = [], tableData = [];

        var suitesJSON = getSuites();

        suitesJSON.done(function(data) {

            var suites = data.QueryResult.Results;

            for(var i = 0; i < suites.length; i++) {

               (function(i) {
                    var defectsJSON = getDefects(suites[i].Defects._ref + "?fetch=Name,FormattedID,State,Priority,CreationDate,c_RootCause,c_RootCauseCRM");
                    ajaxCalls.push(defectsJSON);

                    defectsJSON.done(function(data) {
                        var defects = data.QueryResult.Results;

                        for(var j = 0; j < defects.length; j++) {
                            tableData.push({
                                "suiteName": suites[i].Name, // This is the name of the suite collected in the outer call
                                "defectName": defects[j].Name,
                                "FormattedID": defects[j].FormattedID,
                                "State": defects[j].State,
                                "Priority": defects[j].Priority,
                                "CreationDate": defects[j].CreationDate,
                                "RootCause": defects[j].c_RootCause,
                                "RootCauseCRM": defects[j].c_RootCauseCRM
                            });
                        }

                    });
               })(i);

            }

        });


        $.when.apply($, ajaxCalls).then(function() {
            console.log(tableData);
            table.appendRows(tableData);
            doneCallback();
        });
};

2 Answers2

1

You should use a better model to get multiple items. Using a for loop to query for multiple gets is the problem, and the solution should be to refactor so that you make one request that returns everything you need.

If this doesn't seem possible to you, I've researched a way to do what you want in jQuery.

$.when(
    $.get(path, callback), $.get(path, callback), $.get(path, callback)
.then({
    //This is called after all requests are done
});

You could create an array of all your requests like [$.get(path, callback), request2, request 3, etc...] and then use the spread method to put them as arguments like

var args = [$.get(path, callback), request2, request 3, etc...];
$.when(...args).then(() => {/*call here*/});

This link has the rest of the information https://css-tricks.com/multiple-simultaneous-ajax-requests-one-callback-jquery/

Zachary Brooks
  • 303
  • 2
  • 9
  • Yeah, the multiple gets isn't great, but the problem is that each suite provides a URL reference to the defects inside the suite, and the only way to access these defects is to make another API call (as far as I know). I could definitely be overlooking something though – James Prescott May 31 '19 at 18:56
  • Also, aren't I already filling an array with requests (ajaxCalls) and then using $.when to call something when they are all done? Or am I not using it in the right way? In this post https://stackoverflow.com/questions/8097516/how-to-wait-until-nested-async-jquery-ajax-requests-have-finished?rq=1 the OP seems to be doing something similar to me – James Prescott May 31 '19 at 19:06
  • I added the last part based off a comment - I didn't realize it was already in there. I'll keep the answer to help someone in the future with a similar problem. – Zachary Brooks May 31 '19 at 19:15
0

I think the problem is that you are calling $.wait right after getSuites() is executed.

$.wait 'sees' the ajaxCalls array empty (because getSuites() hasn't finish yet) and executes doneCallback().

Try to call $.wait INSIDE the suitesJSON.done function, that way it will be called after the ajaxCalls array is filled with the first response:

myConnector.getData = function (table, doneCallback) {
    console.log("Getting Data...");
    var ajaxCalls = [], tableData = [];

    var suitesJSON = getSuites();

    suitesJSON.done(function(data) {

        var suites = data.QueryResult.Results;

        for(var i = 0; i < suites.length; i++) {

           (function(i) {
                var defectsJSON = getDefects(suites[i].Defects._ref + "?fetch=Name,FormattedID,State,Priority,CreationDate,c_RootCause,c_RootCauseCRM");
                ajaxCalls.push(defectsJSON);

                defectsJSON.done(function(data) {
                    var defects = data.QueryResult.Results;

                    for(var j = 0; j < defects.length; j++) {
                        tableData.push({
                            "suiteName": suites[i].Name, // This is the name of the suite collected in the outer call
                            "defectName": defects[j].Name,
                            "FormattedID": defects[j].FormattedID,
                            "State": defects[j].State,
                            "Priority": defects[j].Priority,
                            "CreationDate": defects[j].CreationDate,
                            "RootCause": defects[j].c_RootCause,
                            "RootCauseCRM": defects[j].c_RootCauseCRM
                        });
                    }

                });
           })(i);

        }

        $.when.apply($, ajaxCalls).then(function() {
            console.log(tableData);
            table.appendRows(tableData);
            doneCallback();
        });
    });
};
kidkamek
  • 426
  • 4
  • 7