1

I've tried Googling this but could not reslove it. It may seem like a really simple issue to others but I'm baffled by it. I have the below code in which I get undefined for the first alert but I still get the correct values in the 2nd alert. BUT if I comment out the first alert (just the line with alert) then the 2nd alert output becomes undefined. Can any one explain why this is and how I may output the 2nd alert correctly without the first one, any Help is greatly appreciated.

function getDetails(ID){
        var qArray = [];
        $.get('get_Question', {"Id":ID}, function(){})
        .success(function(data){
            var json = $.parseJSON(data);
            qArray.push(json.value1);
            qArray.push(json.value2);

        });
        //First Alert
        alert("-> "+qArray[0]);
        return qArray;
     }

This is the 2nd alert which calls the above method:

var myArray = getDetails(4);
alert("myArray [0]: "+myArray[0]);
jonnie
  • 12,260
  • 16
  • 54
  • 91
  • 3
    `.get` is an asynchronous call - you cannot return a value. Execute all of your code in the success function – Manse Jan 03 '13 at 11:54
  • This won't work. The first A in AJAX means "asynchronous", as in "we can't wait for the result". – John Dvorak Jan 03 '13 at 11:54
  • @JanDvorak Yes you can. (But you shouln't!) Don't let the acronym fool you. The 'X' is for XML, while you can perfectly well return JSON or (other) plain text. :p – GolezTrol Jan 03 '13 at 11:57
  • @GolezTrol would you mind if I coin the term "SJAX" for "Synchronous Javascript And XHR"? – John Dvorak Jan 03 '13 at 11:59
  • XHR, which should be JHR, THR or just HR. :) Yeah, the MS Outlook developer who invented the name, didn't think of what we might use it for. :) – GolezTrol Jan 03 '13 at 12:00
  • 2
    possible duplicate of [return from jquery ajax call](http://stackoverflow.com/questions/2844832/return-from-jquery-ajax-call) and [nearly 1000 other questions](http://stackoverflow.com/search?q=jquery+return+value+from+ajax+call). FYI, this kind of question comes up every day... – Felix Kling Jan 03 '13 at 12:02
  • @GolezTrol anyways, the term AJAX has since evolved from an acronym to a capitalised noun :-) – John Dvorak Jan 03 '13 at 12:03
  • @FelixKling it's a duplicate, sure, but sometimes the devil's in the detail (i.e. my use of `.pipe` to convert from the structure in the transmitted JSON to the array format required). – Alnitak Jan 03 '13 at 12:30
  • @Alnitak: I agree, and I think your answer is great, but we cannot answer *all* of those questions. Maybe we need a proper canonical answer which also explains how to use deferred objects in this case. – Felix Kling Jan 03 '13 at 12:34

7 Answers7

9

You can't return a value, the $.get() call is asynchronous.

You need to defer any operations on qArray until the AJAX call has completed, i.e. inside the callback.

Better yet, use deferred callbacks:

function getDetails(ID) {
    return $.get('get_Question', {"Id":ID})
            .pipe(function(json) {
               return [json.value1, json.value2];
             });
}

The .pipe deferred function creates a new promise which will ultimately return the desired array, but only once the AJAX call has completed.

You would then use this like this:

getDetails(ID).done(function(qArray) {
     alert("-> " + qArray[0]);
});

Note that $.get() doesn't directly support error callbacks, but with deferred objects you can get access to them:

getDetails(ID).done(function(qArray) {
     alert("-> " + qArray[0]);
}).fail(function(jqXHR, textStatus, errorThrown)) {
     alert("The AJAX request failed:" + errorThrown);
});

Without this you'd need to build the error handling directly into the getDetails() function and then require some mechanism to tell the rest of the application logic about the error.

NB I've assumed that you don't really need to call JSON.parse() manually - if your web server returns the right Content-Type header then jQuery will do that for you automatically.

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • This is a very elegant way to do it! – Emil Ivanov Jan 03 '13 at 12:01
  • Even if the server doesn't return the correct content-type, you can force one. – John Dvorak Jan 03 '13 at 12:04
  • @Alnitak Thank you, this is very useful. The only thing is I still do not understand why I my code above can I get the value of the array in the 2nd alert if the first alert is there. Can you explain that Please? – jonnie Jan 03 '13 at 12:58
  • 1
    @jonnieM it's just because the first alert gives sufficient time for the AJAX call to complete, so the array is filled by the time you call alert the second time. – Alnitak Jan 03 '13 at 13:30
4

Ajax calls happens asynchroniusly, meaning you can't wait for the call to return and get the value. The way to do it is to employ a callback. Your example will become something similar to this:

function getDetails(ID, callback){
    $.get('get_Question', {"Id":ID}, function(){})
    .success(function(data){
        var qArray = [];
        var json = $.parseJSON(data);
        qArray.push(json.value1);
        qArray.push(json.value2);
        callback(qArray)
    });
 }

Calling it will change a bit:

getDetails(4, function (myArray) {
    alert("myArray [0]: "+myArray[0]);
});
Emil Ivanov
  • 37,300
  • 12
  • 75
  • 90
  • @GolezTrol actually, no, passing callbacks is the "old" way of doing this. – Alnitak Jan 03 '13 at 11:58
  • @Alnitak What is the new way? – Amit Garg Jan 03 '13 at 11:59
  • @AmitGarg promises. They're like callbacks, only you pass the callback to the return value (which means that passing the callback can wait). – John Dvorak Jan 03 '13 at 12:00
  • @AmitGarg see my answer. In fact the OP is already using promises, he maybe just doesn't know it. He has both passed a callback function (the old way) and called the (deprecated) `.success()` function. – Alnitak Jan 03 '13 at 12:01
  • @AmitGarg and importantly, they allow you to _decouple_ the act of making the AJAX call from what's subsequently done with the results of that call. – Alnitak Jan 03 '13 at 12:04
  • @Alnitak Is the decoupling actually used in practice? – John Dvorak Jan 03 '13 at 12:05
  • @Alnitak Since callbacks added after the promise is resolved are still triggered, it seems _very_ powerful. Without that retroactive feature, I couldn't imagine much use. – John Dvorak Jan 03 '13 at 12:33
  • 1
    @JanDvorak there's much more to deferreds than that. For example, `$.get` doesn't expose an error callback parameter, but the returned promise _does_. An AJAX helper function should only be concerned with placing the call with the correct parameters, not manipulating the data. Also, you can trivially register multiple handlers, synchronise multiple async events, all sorts of stuff that you can't do easily with a "success" callback passed as a parameter. – Alnitak Jan 03 '13 at 12:38
  • @jonnieM this wasn't my answer – Alnitak Jan 03 '13 at 13:29
  • @EmilIvanov please see my updated answer and consider how you would perform error handling with your version... – Alnitak Jan 03 '13 at 14:23
2

The First Alert is called before the ajax call is finished, so the variable is still undefined. This is because the $.get() is done asynchronously. There is no option for $.get() to pass parameter for async calls, so you should use $.ajax() instead and pass a param async: false

Vassilis Barzokas
  • 3,105
  • 2
  • 26
  • 41
1

The $.get call creates a new asynchronous request for the resource in question.

When you call the first alert it is undefined because the request hasn't been completed yet. Also since you are forced to pause on the alert the request has time to be completed in the background. Enough time for it to be available by the second alert.

The same thing happens when you comment out the first alert. This time the second alert is called before the request is completed and the value is undefined.

You need to either make your requests synchronous or consider continuing execution after receiving the response by using a callback function within the success callback function you have already defined in $.get.

Tanzeel Kazi
  • 3,797
  • 1
  • 17
  • 22
1

As several others have said, ajax-request are asynchronous. You could however set the async property to false to get a synchronous request.

Example:

function getDetails(ID) {
   var result = $.ajax('get_Question', { 
      async : false, 
      data : { 'Id' :  ID }
   });

   // do something with the result

   return result;
}

I myself would have use a callback function instead beacuse async:false is bad practice and is also deprecated.

nekman
  • 1,919
  • 2
  • 15
  • 26
0

You'll need to rewrite $.get to use $.ajax and specify async: false

scottlimmer
  • 2,230
  • 1
  • 22
  • 29
  • 3
    synchronous AJAX calls are due to be deprecated. – Alnitak Jan 03 '13 at 11:54
  • 1
    That is an option, but rather write a callback handler and put the code in there. Only do synchronous calls if you really, really need to, which is virtually never. – GolezTrol Jan 03 '13 at 11:55
  • @GolezTrol you could need synchronous request if... Yeah, I don't know of a good use case either. – John Dvorak Jan 03 '13 at 11:57
  • 1
    @jonnieM Alert() will let enough time to request to be completed. Alert() should never been used in debug purpose. – A. Wolff Jan 03 '13 at 11:58
  • @Alnitak Fair point, I made the assumption since a success callback was already in place, there was a reason why it wasn't already in that callback – scottlimmer Jan 03 '13 at 11:58
0

AJAX is asynchronous: you can't tell when the request will complete. This usually means you need to pass callback methods that will be called with the result of the request when it completes. In your case this would look something like:

function getDetails(ID, callbackFunc){
    $.get('get_Question', {"Id":ID}, function(){})
    .success(function(data){
        var qArray = [];
        var json = $.parseJSON(data);
        qArray.push(json.value1);
        qArray.push(json.value2);
        callbackFunc(qarray);
    });
 }

 getDetails(4, function(qArray) {
     alert("myArray [0]: "+qArray[0]);
 };
Christophe L
  • 13,725
  • 6
  • 33
  • 33