views:

86

answers:

5

I have wrapped a common ajax call into a function. It pings a script, returns JSON.

However for the life of me I can't seem to be able to have the JSON object be the return value of the function.

Must be something fairly simple I am missing, bu for the life of me can't work it out.

function queryCostCenter(user_id, currency_id, country_id){

   var output = null;
   var destinations = new Array();

   var destination = { qty:1, country: country_id };
   destinations.push(destination)           


   var data = {
                 destinations : $.toJSON(destinations),
                 user_id : user_id,
                 currency_id: currency_id
              };

   $.ajax({
         data: data,
         type: 'POST',
         url: '/lib/ajax/ajax_prepaid_cost_calculator.php',
         success: function(data) {         
            output = data;
            alert(output);
         }
   });

   alert(output);

   return json;

}

The alert() inside the ajax() call displays the json object, however if try and alert outside the function, and/or return the response from inside the ajax() call its value is null?!

Any help/pointers would be appreciated.

+2  A: 

$.ajax() is async. In short: the alert(output) outside your function block will most likely be called before the async method returned any result, therefor is still null.

Bart
+1 first w/correct answer
danlefree
@danlefree You can upvote all the correct answers if you want, not just the first one. :)
bzlm
+8  A: 

Typical mistake. The code after the Ajax call

alert(output);
return json;

is executed, before the Ajax call returns. It is asynchronous (meaning, it is not executed when and where you put it in the code, but at some later point in time). You can provide a callback to your function, like so:

// cb is our callback - it is a function
function queryCostCenter(user_id, currency_id, country_id, cb){ 
   var destinations = new Array();

   var destination = { qty:1, country: country_id };
   destinations.push(destination)           

   var data = {
                 destinations : $.toJSON(destinations),
                 user_id : user_id,
                 currency_id: currency_id
              };

   $.ajax({
         data: data,
         type: 'POST',
         url: '/lib/ajax/ajax_prepaid_cost_calculator.php',
         success: function(result) { // or just `success: cb`
            cb(result); // execute the callback with the returned data
         }
   });   
}

then:

queryCostCenter(some_value, some_value, some_value, function(result) {
    // do something with the returned data.
});

Or put all the logic in the success handler of the Ajax call. But with a callback function, you are more flexible and you can better reuse the function.


This a very common use case for callbacks. As you don't know when the Ajax call will be finished, you pass a function to the Ajax call that should be run, when some results is returned. Your are doing nothing else with the success handler: It is a function that is called when the call is finished.

Felix Kling
+1 most thorough + correct answer
danlefree
You should mention the 'async' option.
sje397
I'll mention it. If you set the 'async' option to false, then the script will halt until the request is finished. Add `async: false,` above where you have `data: data` and your code works as is. Here's some reading. http://api.jquery.com/jQuery.ajax/
dustynachos
@sje397: Honestly, I think the `async` option is nonsense. I did not do any measurements, but I cannot imagine that an Ajax request will be finished within 100ms (which is the maximum time a piece of code should run, otherwise the UI will seem to be slow for the user). There *might* be situations, where this option has an advantage. But the whole point of an Ajax request is to be asynchronous. For new developers in this area, the `async` might be tempting to use, because they don't know better... I would try to avoid it.
Felix Kling
@Felix King: I totally agree - that's the sort of mention I was thinking of. Since it's easy to find in the docs, it's worth talking about I think. I've never had cause to use it. Though it does 'make the code work' in this case, it's a much better option to refactor the code to use a callback as you've shown.
sje397
A: 

you aren't actually fetching and converting back to JSON, just a big text blob. look at the documentation for getJSON, which is what you need to use.

http://api.jquery.com/jQuery.getJSON/

$.getJSON({
     '/lib/ajax/ajax_prepaid_cost_calculator.php',
     {data:data}
     , function(data) {   
        alert(data.jsonElement);
        //do something with your data HERE

     }

});

with jsonElement being an element in your JSON array

FatherStorm
+1  A: 

It's not a matter of scope. It's the asynchronous nature of your Ajax call. Your queryCostCenter function will likely return before the Ajax success handler runs. You need to kick off all post-Ajax logic in your success callback.

Matt Ball
A: 

It's not about scope, it's about that $.ajax() will create an asyncronous request by default.

That means at the time your alert() fires, output is still null.

To have a return like value from an asyncronous callback, you need to invoke another callback yourself!

Example:

function queryCostCenter(user_id, currency_id, country_id, callback){ 
    // do all your stuff here

     $.ajax({
        data: data,
        type: 'POST',
        url: '/lib/ajax/ajax_prepaid_cost_calculator.php',
        success: function(data) {         
           if (typeof callback === 'function')
               callback.apply(this, [data]);
        }
     });
}

Now you can call your function like:

queryCostCenter(5, 55, 33, function(data){
    output = data;
    alert(output);
});
jAndy