0

I have a couple of functions that use a jQuery AJAX call to retrieve an XML file from my server, and convert it into something usable. Look at the following code:

getBalanceXML: function (callback) {
    $.ajax({
        type: 'GET',
        url: 'Content/saldotracking.xml', 
        dataType: 'xml',
        success: callback,
        error: function () { throw new Exception("getBalanceXML(): Failed to load XML file"); }
    })
},

getBalanceBarChart: function (xml) {
    var balanceArray = [];
    $(xml).find('Balance').each(function () {
        var nodeObject = {
            //fill a JavaScript object with values from the XML file in the format I need
            }
        balanceArray.push(nodeObject);

    });
    console.log(balanceArray);
    return balanceArray;
}

Now, when I call this function with:

var stuff = getBalanceXML(getBalanceBarChart);
alert(typeof(stuff));

The stuff var is still undefined. Following the other answers on StackOverflow, I pass the getBalanceChart function as a parameter to the getBalanceXML function, which does the AJAX call. Success! Now getBalanceChart can also use the XML file gained from the AJAX call. However, all I've done is move the synchronization problem to another function. There still comes a point where a synchronous part of my program needs the array that comes from an asynchronous function.

What do I do?

yesman
  • 7,165
  • 15
  • 52
  • 117
  • 1
    Read this thread: http://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-ajax-call – epascarello Sep 26 '14 at 12:44
  • You cannot. It's outright impossible. If you use asynchronous data, your whole program does get asynchronous. – Bergi Sep 26 '14 at 13:29

2 Answers2

1

EDITED to add code

The problem is that at this line, the assignment happens before the function completes, hence the "undefined" result

var stuff = getBalanceXML(getBalanceBarChart);

If I understand your need correctly, this should sort you out :

  1. make stuff a globally accessible variable

  2. In your getBalanceBarChart do not return the value, instead assign it to stuff directly

  3. (optional) you can then, still in the getBalanceBarChart function, trigger some other action you may want to take.

Basically, by doing both the Assignment of the result and Triggering of next action, at the end of the callback function, you guarantee that the asynchronous part has completed.

Hope that helps.

getBalanceBarChart: function (xml) {
var balanceArray = [];
$(xml).find('Balance').each(function () {
    var nodeObject = {
        //fill a JavaScript object with values from the XML file in the format I need
        }
    balanceArray.push(nodeObject);

});
console.log(balanceArray);

// DO not return value, instead assign it to a variable directly,
// since this will only occur after any asynchronous processing

myGlobalVariable = balanceArray;

// optional : someOtherFunction();
}
LavenPillay
  • 145
  • 1
  • 1
  • 9
  • Setting a variable outside of the function chain instead of returning anything works great! Thanks for your help. – yesman Sep 26 '14 at 13:31
0

What's happening is that you are assigning to stuff the value returned from getBalanceXML, which is nothing, or undefined. The AJAX call is non-blocking, so getBalanceXML fires the AJAX and exits the function right away.

I would do something like this:

$.ajax({
    type: 'GET',
    url: 'Content/saldotracking.xml', 
    dataType: 'xml'        
}).done(function(xml) {
    var balanceArray = [];
    $(xml).find('Balance').each(function () {
        var nodeObject = {
            //fill a JavaScript object with values from the XML file in the format I need
        }
        balanceArray.push(nodeObject);
    });
    console.log(balanceArray);

    // Do something with balanceArray
    // ex. updateBalance(balanaceArray);

}).fail(function() {
    throw new Exception("getBalanceXML(): Failed to load XML file");
});
derekmckinnon
  • 524
  • 1
  • 5
  • 15
  • This solution works for me (I tried), but it leaves you with a giant function that does a whole lot of different things, making it hard to test and maintain. – yesman Sep 26 '14 at 13:31
  • I'm sure it could use a bit of refactoring, but I don't see how it would be any less testable than what you had before. If your needs are more complex and you wanted databinding or something, then Backbone/Angular/Ember would go a long way to increasing the maintainability of your code. But if it's simple, then those would be overkill for sure. – derekmckinnon Sep 26 '14 at 22:16