0

Here is some code.

function callServiceSync(url, obj) {
    var result = true;
    callServiceOptions(url, obj, function(res){
        result = res;
        alert("RESULT CB: "+JSON.stringify(result));
    }, {async: false});
    alert("RESULT POST-CB: "+JSON.stringify(result));
    return result;
}

When it runs, the alert box says:

RESULT CB: {"success":true,"data":"dtrombley"}

(that's what the webservice returns, in reality), and then:

RESULT POST-CB: true

Why isn't this assignment to the closure variable working? Am I misunderstanding how JS closures work?

callServiceOptions() is rather longwinded - but the gist of it is that it calls jQuery's $.ajax method with it's last arguments options extended into some default (in this case, async is disable for sync query), and then executes the provided callback.

Is $.ajax() maybe executing something in some kind of way that disables/screws up closures (but I call the cb, not $.ajax()!)? If so, how to fix that?

For completeness (though really this function shouldn't be able to screw things up to my thinking):

function callServiceOptions(url, obj, cb, options) {
    optSuccess = options.success;
    optError = options.error;
    opts = {}
    $.extend({},options)
    if (!opts.contentType) {
        opts.contentType = "application/json";
    }
    if (!opts.dataType) {
        opts.dataType = "json";
    }
    if (!opts.data && obj) {
        opts.data = JSON.stringify(obj);
    }
    if (!opts.processData) {
        opts.processData = false;
    }
    if (!opts.method) {
        opts.method = "POST";
    }
    opts.error = function(jqXHR, textStatus, errorThrown) {
        if (optError) {
            optError(jqXHR, textStatus, errorThrown);
        }
        if (jqXHR.responseText) {
            responseObj = JSON.parse(jqXHR.responseText)
            if (responseObj && responseObj.message)
            cb({
                success: false,
                message: responseObj.message
            })
            return
        }
        cb({
            success: false,
            message: errorThrown
        });
    };
    opts.success = function(data, textStatus, jqXHR) {
        if (optSuccess) {
            optSuccess(data,textStatus,jqXHR);
        } 
        cb(data);
    };
    if (url.charAt(0) == '/') {
        url = url.substr(1);
    }
    opts.url = WEBCTX.getBaseURL() + url;

    $.ajax(opts);
}

This is not a duplicate of any question asking how to return a value from an async event. I have a working callServiceAsync() which does that beautifully. I am using synchronous mode, if you aren't familiar with it, please take a pass on this question...

BadZen
  • 4,083
  • 2
  • 25
  • 48
  • Quentin - Please read the question more carefully. I'm not executing an asynchronous call, I'm executing a synchronous call, and the value is getting returned correctly from the jQuery ajax(). – BadZen May 12 '16 at 21:20
  • 1
    No, there is a synchronous query mode, and that is what I am using. Please read the documentation at http://api.jquery.com/jquery.ajax/ and especially the bit about the "async" option, which I am using. – BadZen May 12 '16 at 21:22
  • If it were async, the alerts would not return in that order, but the opposite. – BadZen May 12 '16 at 21:23
  • I don't see anything in your code that says to use synchronous requests... – Niet the Dark Absol May 12 '16 at 21:31
  • 1
    Your comments look a bit (fill in a fitting word here) now. ;-) – trincot May 12 '16 at 21:36
  • If you're going to do sync ajax do it in a web worker. – r3wt May 12 '16 at 21:40
  • @trincot - Haha yup. Originally was marked dup, which it def was not. Funny in hindsight though. Think leaving them is better, since more info for posterity. – BadZen May 12 '16 at 21:42

1 Answers1

6

Your function is asynchronous.

While you have created an object that looks like {async: false}, you are passing it as the 4th argument to callServiceOptions so it gets placed in the options variable.

You only access that variable twice (options.success and options.error) so the async property is never used for anything (so $.ajax uses the default value of true).

Adding console.log(opts) just before you call $.ajax(opts); will show this.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • Ah my mistake. I saw the `extend` logic, I was thinking the options was getting extended into opts. – jeffjenx May 12 '16 at 21:32
  • 2
    @Quantastical, `$.extend` modifies the first argument and returns it, but you don't capture the return value. – trincot May 12 '16 at 21:34
  • Ook. So was I. `$.extend(opts,options)` is what I meant of course. Shame =) Works now. – BadZen May 12 '16 at 21:34
  • (So, the order of the alerts are not always linear in the order of execution? In FF, anyhow...) – BadZen May 12 '16 at 21:41
  • (that's what *asynchronous calls* have as effect: asynchronous events are put on the event queue; but the current running code is first completed and only then the event queue is processed, triggering the call back functions) – trincot May 12 '16 at 21:57
  • Yes, I understand how async calls work. However, in this case if you reread my observed behavior you'll see that the second-called alert() was displaying /first/. – BadZen May 12 '16 at 22:04