3

In the following, the return statement is being executed before the call to $.get() finishes. How can I change this?

var aft = {};

aft.getToken = function (url) {

    var theToken;

    //Get the token value
    $.get(url, function (data) {

        theToken = $(data).find('input[name=__RequestVerificationToken]').val();

    }, "html");

    return theToken;

};
ThinkingStiff
  • 64,767
  • 30
  • 146
  • 239
epaulk
  • 309
  • 1
  • 4
  • 15

3 Answers3

4

jQuery supports this out of the box, set the option async to false and the function call will be synchronous, instead of asynchronous.

Though I'd recommend you to keep the request async, see the "recommended solution" at the end of this post.

async (Boolean) Default: true

By default, all requests are sent asynchronously (i.e. this is set to true by default). If you need synchronous requests, set this option to false. Cross-domain requests and dataType: "jsonp" requests do not support synchronous operation.

Note that synchronous requests may temporarily lock the browser, disabling any actions while the request is active.


But I'm using $.get, that seems to be specific for $.ajax?

Yes, but under the hood they are exactly the same thing.

jQuery.get ():

This is a shorthand Ajax function, which is equivalent to:

$.ajax({
  url: url,
  data: data,
  success: success,
  dataType: dataType
});

Sample implementation

aft.getToken = function (url) {
  var theToken;

  $.ajax({
    url: url,
    dataType: 'html'
    success: function (data) {
      theToken = $(data).find('input[name=__RequestVerificationToken]').val();
    },
    dataType: dataType
  });

  return theToken;
}

Recommended solution

As stated by the previously mentioned comment from the jQuery documentation asynchronous requests are normally NOT recommended, and will most often to more harm than good.

Instead you should try to implement the functionality asked for by using a callback or similar.

A callback is a function passed to some other function to handle a certain event, such as the success of your data retrieval. Just as you are passing a callback to $.get in your own snippet.

Make your function aft.getToken accept a second parameter named callback which should be a function reference. Then call this function with the token replied by your ajax request as a parameter when data retrieval and "parsing" is finished.

aft.getToken = function (url, callback) {
  var dst_object = this;

  $.get (url, function (data) {
    dst_object.stored_token = $(data).find (
      'input[name=__RequestVerificationToken]'
    ).val ();

    callback (dst_object.stored_token);
  }, "html");
};

...

aft.getToken ('/path/to/file.html', function (token) {
  console.log ("Got token: " + token);
});

...

console.log (aft.stored_token);
Filip Roséen - refp
  • 62,493
  • 20
  • 150
  • 196
  • This should really be a last-ditch solution that you only implement if callback functions or code reorganization absolutely won't work. – jwheron Dec 23 '11 at 01:15
  • 1
    Do *not* use synchronous requests. Instead learn how to program using the event model, which JavaScript in browsers is pretty much based on. – Matti Virkkunen Dec 23 '11 at 01:19
  • 2
    @MattiVirkkunen Maybe instead of commenting on everyone's answers about what the OP shouldn't do, you could provide a useful answer to solve the OP's problem that meets your standards. – ThinkingStiff Dec 23 '11 at 01:54
  • @ThinkingStiff: Adam Rackis already did that and I didn't feel like posting the same thing again. – Matti Virkkunen Dec 23 '11 at 02:49
  • @MattiVirkkunen Our posts are very different in style and readability, as well as mine being more elaborate with links to sources where information is found (even if I pulled it out of the back of my head). – Filip Roséen - refp Dec 23 '11 at 06:36
  • @MattiVirkkunen First priority should be to answer OPs question, providing him with "tips N tricks"/nicer/recommended solutions is a bonus. – Filip Roséen - refp Dec 23 '11 at 10:34
  • @refp: No, if the OP is asking for something that's wrong, the first priority is to tell them why they're wrong, and then perhaps explain the correct way to them. – Matti Virkkunen Dec 24 '11 at 09:09
4

You can't—at least not sensibly. While there is an async property you can set on jQuery ajax requests, I've had serious problems trying to use it with a false value in the past.

Try to re-think what you're trying to accomplish:

var aft = { yourToken: '' };

aft.setToken = function (url, callback) {
    $.get(url, function (data) {
        this.yourToken = $(data).find('input[name=__RequestVerificationToken]').val();

        if (callback)
           callback.apply(this);
    }, "html");
};

And then:

aft.setToken("url.php", function() {
   alert("Token retrieved = " + this.yourToken);
});

Or, if your callback only needs access to the returned data, then you could more simply do

if (callback)
    callback(data);

And then

aft.setToken("url.php", function(dataReturned) {
   alert("Ajax data retrieved = " + dataReturned);
});
Adam Rackis
  • 82,527
  • 56
  • 270
  • 393
  • This is the closest to correct answer so far, but I don't see the point of using an object and `apply` to pass the value. Isn't it enough just to do `callback($(data)...);`? – Matti Virkkunen Dec 23 '11 at 01:20
  • @MattiVirkkunen - that would work too. Probably simpler. Using apply gives the callback access to the rest of the object, if there's anything else the callback cares about – Adam Rackis Dec 23 '11 at 01:21
  • @MattiVirkkunen Can I ask why you don't seem to think my answer is as accurate? – Filip Roséen - refp Dec 23 '11 at 01:28
  • 1
    @refp: Because blocking calls (such as synchronous requests) do not belong in the event-based world of JavaScript in browsers. The whole option of making a request like that is a mistake, and I hope it gets deprecated in the future. – Matti Virkkunen Dec 23 '11 at 01:29
  • @MattiVirkkunen so, if a user on SO asked for one thing we should try not to answer his questions properly but lead him in another direction? ("I'd like to pass a reference to a variable such as `var x = 1;` in javascript, how is that possible?" => "You can't, instead switch to C/C++ where this is most def' possible, it's much better.. javascript is for loosers". I provided OP with a detailed answer including an alternative/recommended solution, which too me is good enough to be regarded as a "correct" one. – Filip Roséen - refp Dec 23 '11 at 01:37
  • 1
    @refp: There's nothing wrong with `var x = 1;`. However, there are many things wrong with synchronous requests. Just admit defeat and move along. – Matti Virkkunen Dec 23 '11 at 01:37
  • It was a simile, and obviously one which you didn't comprehend properly/applied the current situation in. I'm not facing defeat nor am I winning (pun intended), I answered the question and that's all I'm here to do. Though saying that this answer in question is "closer" to a correct one is, at least to me, a most stupid statement. – Filip Roséen - refp Dec 23 '11 at 01:42
  • Forgot to mention your nick, and please excuse my poor writing.. @MattiVirkkunen – Filip Roséen - refp Dec 23 '11 at 01:53
0

The callback code by @Adam Rackis is working for me. In fact, if the content of the url is of json structure, you can directly refer the fields like this. Note that aft = { yourToken :'' } is just dummy since I am not using the value in this dictionary at all. I just want to get the user_id from the URL.

var aft = { yourToken: '' };
    aft.setToken = function(url, callback){
        $.get(url, function(data) {
            user_id = data[0]['user_id'];
            if (callback)
                callback(data);
        });
    };

    aft.setToken("https://XXXXXX.azurewebsites.net/.auth/me",  function(dataReturned){
        alert("You user id is " + labeler);
Hang Zhang
  • 13
  • 1
  • 3