views:

85

answers:

6

Let's say you have a Javascript function that calls a web service method. So that webservice completes and calls a callback function, which has the result.

How do I get that result back into the original function that called the web service method? Essentially, I'm trying to "synchronize" an asynchronous call.

Update:

This is what I'm trying to do. I'm validating based on the return value of a web service.

$.validator.addMethod("zzz",
    function(value, element) {

        // Call web service function using ASP.NET Ajax.
        // Get callback value to determine validity.

        return this.optional(element) || ***return t/f based on validity***;
    },
    function(value, element) { return msg; }
);

so I guess I could do this instead:

$.validator.addMethod("zzz",
    function(value, element) {

         $.ajax({
              async: false
              url: **** web service url ****
              success: set t/f to validity var
         });

        return this.optional(element) || ***return t/f based on validity var***;
    },
    function(value, element) { return msg; }
);
+1  A: 

Essentially, you can't, but you can break up that function into "before" and "after" parts, like so:

function foo(a) {
    var b, c;

    // == The "before" part:

    b = /* ... */;

    // == Call the service:

    callWebService(a, b, callback);

    // == The "after" part is contained within the callback:

    function callback(d) {
        // Do something with a, b, c, and d
    }
}

But it's important to note that foo will return before callback is called. There's no way to prevent that with an asynchronous call, so if you're looking to have the function return something, you'll have to refactor (or use a synchronous call, but that's a very bad idea). The refactoring would involve whatever's calling foo to provide a callback and expect the result to be provided that way rather than as a return value.

T.J. Crowder
+1  A: 

Well what you're trying to accomplish is simulating a sleep command, so your script "waits" for your ajax request? But that doesn't really makes sense. That's why you have to callback in the first place, to continue with the flow once the request has returned a reply, since you cannot predict its response time.

treznik
+1  A: 

Not to be trite, but you can't create synchronousness from asynchronousness, only the other way around. You need to design your code to allow for this, which generally means callbacks all the way through your call chain.

There is one idiosyncratic exception, which is that you can specify 'false' on the raw XMLHttpRequest's 'open' method's async parameter, which will cause the send method to block until it's done. But this is likely not compatible with some frameworks and is pretty inflexible. Most JS stuff is async.

quixoto
A: 

The only way I can think of is like

function b(){
    var a = null;
    $.get("abc.json", {
        success: function(data){
            a = data;
        }
    });
    while(a == null){}
    alert(a);
}
b();

But generally this isn't good and may cause the browser to complain a script is taking too long to finish.

coolnalu
Besides the fact that this will assure your spot in hell, you should either have `a` to be undefined at first or have an auxiliary var to check, because the callback response might still be `null` and the loop would never end.
treznik
Absolutely. I should add that this code should never be used for any useful product. I just wrote it for theoretical purposes.
coolnalu
In addition to being a *really bad idea*, this doesn't even work. Javascript on browsers is *single-threaded* (for now; web worker threads are coming). That means that although the GET will complete and the browser's Javascript interpreter will queue the success callback, because you've got the only thread tied up in a CPU-burning busy-loop, that callback will never get executed and the loop will never end. Further, if the request takes a while, you'll get the dreaded "this script is taking too long" message (on IE it's operations-based, not time-based; even IE should busy-wait pretty fast).
T.J. Crowder
i tested in my firefox and it didn't lock the browser up when the request succeeded.
coolnalu
+2  A: 

Since you're using jQuery, you can use async:false in your ajax command, like this:

$.ajax({
  //options..
  async: false
});
//this code will run after the request returns

Note though, this blocks the UI (locks up the browser), it's better to continue the work that depends on the result in the success callback, like this:

$.ajax({
  //options..
  success: function(data) {
    doSomethingWithResult(data);
  }
});
Nick Craver
+1  A: 

You should not do that. Just carry on with your processing from the point of the callback. You risk hanging the browser completely if the call does not return.

If you control the server side then you could write some code on the js side to aggregate calls and then write something on the server side to unpack and do multiple calls from each nested call in the aggregate. When the responses come back then aggregate those and send them back. This will save on performance since large calls are cheaper than many small calls.

We did that on a project I worked on and it worked very nicely. It also consolidates logic on the js side to not be spread all over due to all the async callbacks.

Romain Hippeau