views:

325

answers:

5

Hi all,

I have a problem with a jQuery ajax call. I need to wait for the call to be finished in order to return a value. However, the script seems to jump ahead and not wait for the call to be over. my function then returns "undefined".

I have tried to use the .ajax() method and set the value of async to false, but this would not work either.

I could I get my function to return the value that I could through the ajax call?

Thank you!

Here is the code:

function get_rsrce_name(){

    jQuery.post(

        '<?php echo admin_url( 'admin-ajax.php' ); ?>',

        { action :'my_action', option_rsrce : 'option_rsrce' },

        function( data ) {

            output =  data.option_name;         

        },

        "json"
    );      

    return output;
}
+2  A: 

The reason it isn't working when you tell it to run synchronously is that the variable 'output' is only defined within the scope of the callback function.

There is almost certainly a better way to do what you're trying to do, but without more context, it'll be tough to explain what that might be.

I'd recommend reading up on "javascript variable scope"

Josh Wright
+1  A: 

Expanding on what Josh said, you have scoping issues with the "output" variable. You could fix that by declaring output at the top of your function like so:

var output;
Keith Rousseau
I don't think scope is the problem, and declaring `output` at the top won't help. The issue is the asynchronous nature of `$.post`.
meagar
The OP mentioned that they made it a synchronous call by setting async: false.
Keith Rousseau
A: 

jQuery's .post() method defaults to running asynchronously, ie: the post() function doesn't stop and wait for the response, it just returns immediately and the rest of your script continues onwards. In this case, it continues onwards to your return statement.

And now we're down to a classic race condition... can the AJAX request complete AND execute the function ( data ) before the rest of the script continues onwards to the return call? Given most networks have latencies measured in milliseconds, and the return call will be executed mere microseconds later, most likely not... the return call will win the race each time.

You'll have to rearrange your code so that the outer function calling up this AJAX request can be re-called with the results of the AJAX request.

A simple method to do this is to make a two-part function:

function do_ajax(parameters, data) {
    if (data == null) {
       jquery.post(
          ....
          success: function(data) { do_ajax(parameters, data); }
       );
    } else {
      ... handle response here ...
    }
}

This way, the function can be called with whatever data you need to pass into the AJAX call (parameters), and the data parameter is initially null. The function sees that data is null, and does the AJAX stuff. Once the response from the server comes back, the function calls itself again, but this with the response data provided, and the other part of the function that handles the data is called.

Marc B
Glad I don't have to maintain your code. That is the most ridiculous way to do it that I could possibly think of. Why would you call do_ajax again instead of just have a function that handles the response?
Keith Rousseau
@Keith I guess it seemed more compact to him? But yea, that's a really round-about way of doing things. Combining two functions into one, overloaded via a huge if/else is a pretty messy idea, let alone having it recursively call itself.
meagar
@Keith. What's the difference between the ajax requester calling itself, or calling another function? Either way, it's got to call a function to do the data processing. At least this way, the requesting and processing logic are kept together. WIth some appropriate wrapper functions so you're not littering the code with repetitive URL definitions and whatnot, I find this far easier to maintain than littering my code with "getX" and "handleX" functions.
Marc B
How could it possibly be better to have a generic function that basically just does an if and shares no logic. Have you ever heard of separation of responsibilities?
Keith Rousseau
A: 

Thank you everybody. Thanks to you I found the solution. I used the synchronous mode and added var output; at the beginning of the function.

Thanks again!

Flo
Could you please mark my answer as the solution then?
Keith Rousseau
A: 

I wouldn't expect your problem to be scoping issues, as JavaScript has function scope, not block scope. Variables declared anywhere in a function are available anywhere else in the function. This is why JSLint bothers you to declare them at the top: Not because it's functionally important, but because it will remind you that your variables are available as though they were declared at the top regardless of where you actually declare them. Try this simple example:

function ajax(callback) { callback(); }
$(function() {
  ajax(function() { x = 7; });
  alert(x); // x == 7
});

The issue is your $.post call is asynchronous. You're reaching the line return output; before the line output = data;. This happens because $.post returns immediately and allows your program to continue, while a potentially slow AJAX call happens. When the AJAX call completes, it fires its complete callback.

You need to restructure your code so that you're doing something with your value inside of the callback function you're passing to $.post. You could accomplish this by passing in a callback parameter to get_rsrce_name():

function get_rsrce_name(onComplete) {
  $.post( url, data, function(data) { onComplete(data); });
}
meagar