burgerB
burgerB

Reputation: 772

Populating an object with ajax in a loop

I need to pull data from a series of .csv files off the server. I am converting the csvs into arrays and I am trying to keep them all in an object. The ajax requests are all successful, but for some reason only the data from the last request ends up in the object. Here is my code:

var populate_chart_data = function(){
    "use strict";
    var genders = ["Boys","Girls"];
    var charts = {
        WHO: ["HCFA", "IWFA", "LFA", "WFA", "WFL"],
        CDC: ["BMIAGE", "HCA", "IWFA", "LFA", "SFA", "WFA", "WFL", "WFS"]
    };
    var fileName, fileString;
    var chart_data = {};
    for (var i=0; i < genders.length; i++){
        for (var item in charts){
            if (charts.hasOwnProperty(item)){
                for (var j=0; j<charts[item].length; j++) {
                    fileName = genders[i] + '_' + item + '_' + charts[item][j];
                    fileString = pathString + fileName + '.csv';
                    $.ajax(fileString, {
                        success: function(data) {
                            chart_data[fileName] = csvToArray(data);
                        },
                        error: function() {
                            console.log("Failed to retrieve csv");
                        },
                        timeout: 300000
                    });
                }
            }
        }
    }
    return chart_data;
};
var chart_data = populate_chart_data();

The console in Firebug shows every ajax request successful, but when I step through the loops, my chart_data object is empty until the final loop. This is my first foray into ajax. Is it a timing issue?

Upvotes: 1

Views: 156

Answers (2)

Kenneth
Kenneth

Reputation: 28737

There are two things you need to consider here:

  1. The AJAX calls are asynchronous, this means you callback will only be called as soon as you receive the data. Meanwhile your loop keeps going and queueing new requests.

  2. Since you're loop is going on, the value of filename will change before your callback is executed.

So you need to do two things:

  1. Push the requests into an array and only return when the array completes
  2. Create a closure so your filename doesn't change

.

var chart_data = [];
var requests = [];

for (var j=0; j<charts[item].length; j++) {
    fileName = genders[i] + '_' + item + '_' + charts[item][j];
    fileString = pathString + fileName + '.csv';
    var onSuccess = (function(filenameinclosure){               // closure for your filename
                        return function(data){
                            chart_data[filenameinclosure] = csvToArray(data);
                        };
                    })(fileName);
    requests.push(                                     // saving requests
        $.ajax(fileString, {
            success: onSuccess,
            error: function() {
                console.log("Failed to retrieve csv");
            },
            timeout: 300000
        })
    );
}

$.when.apply(undefined, requests).done(function () {
    // chart_data is filled up
});

Upvotes: 2

Explosion Pills
Explosion Pills

Reputation: 191779

I'm surprised that any data ends up in the object. The thing about ajax is that you can't depend on ever knowing when the request will complete (or if it even will complete). Therefore any work that depends on the retrieved data must be done in the ajax callbacks. You could so something like this:

var requests = [];
var chart_data = {};
/* snip */
requests.push($.ajax(fileString, {
/* snip */

$.when.apply(undefined, requests).done(function () {
    //chart_data should be full
});

Upvotes: 1

Related Questions