0

Here is a javascript function:

  function getValue(key) {
    var value;
    switch(_options.myType){
      case "cookie":
        value = readFromCookie(key);
        break;
      case "localStorage":
        value = readFromLocalStorage(key);
        break;
      case "db":
        //return value from the server
        // how do I wait for the result?
        $.ajax({
            type: "GET",
            url: "123",
            data: { .... },
            success: function(data){
               value = data; 
            }
        });
        break;
    }
    return value;
  };

In case of sending ajax request, I need to wait untill the ajax request has been completed. How can I do it?

Not that I can't leave the function without having the value calculated. Which means that I can't place a function inside success: handler which will return the value later (or perhaps I don't understand something?). So it must be calculated within the function getValue().

UPDATE

P.S. So how do refactor my code to be able to use callback inside success: handler? Here is the second part of the code:

MyClass123.prototype.myMethod = function(value) {
      //..............
      var var1 = this.getValue("key1");
      if (var1 == "123") { ... }
      else { .... }
      //..............
    }
  };
zx81
  • 41,100
  • 9
  • 89
  • 105
Alan Coromano
  • 24,958
  • 53
  • 135
  • 205
  • It's impossible. Do you know what the word "asynchronous" in "asynchronous javascript and xml" means? It means you _can't wait_ – John Dvorak Jan 27 '13 at 12:36
  • possible duplicate of [jQuery: Return data after ajax call success](http://stackoverflow.com/questions/5316697/jquery-return-data-after-ajax-call-success) – Konstantin Dinev Jan 27 '13 at 12:37
  • If you don't need to return anything from `myMethod`, it's easy. If you do, you need to refactor the code that _uses_ `myMethod`. – John Dvorak Jan 27 '13 at 12:54

3 Answers3

8

See follow-up below

You could make the ajax request synchronous, but that's generally a bad idea (and jQuery won't support it much longer).

Instead, make getValue accept a callback:

function getValue(key, callback) {
   switch(_options.myType){
     case "cookie":
       setTimeout(function() {
           callback(readFromCookie(key));
       }, 0);
       break;
     case "localStorage":
       setTimeout(function() {
           callback(readFromLocalStorage(key));
       }, 0);
       break;
     case "db":
       //return value from the server
       // how do I wait for the result?
       $.ajax({
           type: "GET",
           url: "123",
           data: { .... },
           success: function(data){
              callback(data);
           }
       });
       break;
   }
}

Note that I'm using setTimeout for the readFromCookie and readFromLocalStorage callbacks. Why? Because if getValue might return its value asynchronously, it should always do so. using a timeout of 0 asks the browser to do it as soon as possible once control yields back to the browser (although it's usually clamped to no less than 5-10ms).


Re your comment below:

Have you read what I write? I said that I can't do it for some reason.

I missed that bit, but with all due respect, you can. It may require some refactoring of code, but you can do it, and should. This is how modern web applications work. If your structure doesn't support it, you need to fix the structure. You can use async: false on the request as a temporary workaround until you can do it properly. (Hey, we've all been there and had to do things like that.) Note that it's scheduled for retirement before too long (just jQuery's support of it, you'll still be able to use XHR directly to do synchronous requests).


Re your updated question, here's how you update that code:

MyClass123.prototype.myMethod = function(value) {
  this.getState("key1", function(var1) {
    //                           ^--- Note the var becomes an arg
    if var1 == "123"{ ... }
    else { .... }
  });
};

Note how little it actually changes. The logic that used to follow the call to getState instead goes into the callback you pass into it.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Have you read what I write? I said that I can't do it for some reason. – Alan Coromano Jan 27 '13 at 12:44
  • 2
    @AlanDert: I missed that bit, but with all due respect, you **can**. It may require some refactoring of code, but you *can* do it, and should. This is how modern web applications work. If your structure doesn't support it, you need to fix the structure. (You can use [`async: false`](http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings) on the request as a **temporary** workaround until you can do it properly.) – T.J. Crowder Jan 27 '13 at 12:46
  • +2 for "temporary workaround". It's nothing more than a kludge. – John Dvorak Jan 27 '13 at 12:51
  • @AlanDert: I've updated the answer to show how to modify that code. – T.J. Crowder Jan 27 '13 at 12:55
  • Thanks. Should I not change the code of getValue(key) at all then? – Alan Coromano Jan 27 '13 at 12:59
  • @AlanDert: Yes, you'd change it as shown in the main part of the answer. (The two code snippets go together.) Have to dash, hope this helps, – T.J. Crowder Jan 27 '13 at 13:02
  • as @techfoobar said, it would be helpful to use 'when' and 'then', would not it? So there will be no need to use a callback function. – Alan Coromano Jan 27 '13 at 13:07
  • 1
    @AlanDert - The function you pass to `then` is the same as having a callback. So there is no real difference. You're still doing the rest in a callback with when and then. – techfoobar Jan 27 '13 at 13:13
2

You can make the AJAX request synchronous by specifying async: false - which is not recommended but possible. If it absolutely is the case that your getValue() function should not return before the AJAX request returns, then async is the way to go.

$.ajax({
    type: "GET",
    url: "123",
    async: false // NEW
    data: { .... },
    success: function(data){
        value = data; 
    }
});

Alternative using then and when:

function getValue(key, callback) {
   switch(_options.myType){

     ...

     case "db":
       return $.ajax({ // note the 'return'
           type: "GET",
           url: "123",
           ...
       });
   }
}

and where you call it, call it like:

$.when(getValue()).then(function() {
   // do the rest here ...
});
techfoobar
  • 65,616
  • 14
  • 114
  • 135
2

The right thing to do is to restructure your program flow so you can place your logic in the success handler.

You can make Ajax requests synchronous by using the async option, but that is silly. It'll slow down your app, because the browser window freezes until the request comes back successful (or times out). It's not often used in JavaScript development and for good reason.

Pekka
  • 442,112
  • 142
  • 972
  • 1,088