0

I have two functions. First contains jQuery.get().done() and it's return value is inside done() callback function, as showed below. (I have no idea if using jQuery matters here)

I may not be understanding something, but my functions looks similar to those in first w3 example: https://www.w3schools.com/js/js_async.asp

async function A(){     
  jQuery.get("example.com").done(function(result){
    if (result.indexOf("Example")){
      console.log("sucess");
      return 1;
    } else {
      console.log("Fail");
      return -1;
    }
  })
}

function B(){
  A().then(function(result){
    console.log("Res:" + result);
  })
}

B();

But the output is:

Res:undefined
   // here some time passes for A() to complete.
succes

So I can't understand why .then() doesn't wait for function A() to complete and resolve promise, but instead it acts immediately while result is not defined yet ?

Gbr
  • 29
  • 6
  • 2
    That's because your function `A()` has no rreturn value. Really, you ought to just use the promises built into jQuery and get rid of that use of `.done()`. Use `.then()` instead. Plus, there's no reason to tag a function `async` if you're not using `await` in it. Your function needs to return a promise or it needs to await a promise and then return a value. – jfriend00 Jul 31 '22 at 06:24

3 Answers3

3

This is much better written using actual promises (which jQuery supports) and await as follows:

async function A() {
    const result = await jQuery.get("example.com");
    if (result.indexOf("Example") !== -1) {
        console.log("sucess");
        return 1;
    } else {
        console.log("Fail");
        return -1;
    }
}

Notes:

  1. Stop using jQuery's .done(). It's non-standard. Use .then() or await.
  2. Don't mix await with .done() or with .then(). Use one model or the other.
  3. There's no need for the res intermediate variable. When you get rid of the .done(), you can just return the value directly.
  4. Note, .indexOf() does not return a boolean. It returns -1 if not found and an index of the position if found. Best not to treat its return value as a boolean.
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • 1. I've just read about `.done()` vs `.then()` and you are right that using .then() is better. 2. I've added to my answer purely jQuery solution. 4. Thank you, it's a a bad mistake, it would falsely return true. I've fixed it in my program already, but I forgot about it in testing code. – Gbr Jul 31 '22 at 08:01
  • But if you need HTTP GET status code you still need to retrieve it from the inside of success callback function ? – Gbr Aug 01 '22 at 04:24
  • @Gbr - You showed a coding example with a certain set of requirements. I offered an example to those requirements. Different requirements would naturally result in different code. Personally, I would never use jQuery for http requests as the API is so convoluted, confusing and polluted and full of lots of legacy, not-promise-related stuff. In a browser, I'd just use the `fetch()` interface which is built into every browser and is fully promise-compatible in every way. For an interface with a few more features, I'd probably use `axios()`. – jfriend00 Aug 01 '22 at 04:33
2

This isn't really a jQuery thing in particular. It's just async JS in general. I'm not sure you really need the .done() part at all since you're using promises.

But anyway, you have two basic problems.

  1. You need to return something inside of function A, not inside .done(). So you can return jQuery.get().

  2. In function B, you want to wait for function A to complete before hitting the .then() block. That means function B is also async. (In the simple example below you can omit the async keyword on function A.) You need to declare function B as async and then await the completion of function A.

 

function A() {
  return jQuery.get("example.com").done(function(result) {
    if (result.indexOf("Example")) {
      console.log("sucess", result);
    } else {
      console.log("Fail", result);
    }
  });
}

async function B() {
  await A().then(function(result) {
    console.log("Res:");
    // moved to a separate console log, otherwise dev tools might print
    // a lot of [objectObject] instead of the actual result
    console.log(result);
  })
}

B();

If you want to do other stuff inside of function A and then eventually return the result, you can play around with something more like

async function A() {
  const response = await jQuery.get('example.com');
  // more stuff…
  return response;
}
cjl750
  • 4,380
  • 3
  • 15
  • 27
  • If you return whole jQuery chain, doesn't you return the whole jQuery object as a function result ? But how could I return just a value (1 or -1 as in my code) based on the result of get() request ? – Gbr Jul 31 '22 at 04:22
  • "You need to return something inside of function A, not inside .done(). So you can return jQuery.get()." Why? Is it because if I put a `return` statement in the callback function ,then it's "not seen" from the outside of function A() as a return value of that function? – Gbr Jul 31 '22 at 04:33
  • It returns the response of the GET request. That's how jQuery.get works. Inside function B, in the `then`, you can look through the response and do different stuff at that point. Or you can potentially try a `.then` instead of `.done` inside function A if you want to do that inside function A. Tbh I haven't used jQuery for AJAX before, but the post I linked seemed to suggest you probably want to use jQuery's `.then` function instead. To your second question: yes, exactly. – cjl750 Jul 31 '22 at 04:43
0

I fixed it by moving return statement outside from the .done(function(){}) and by adding await before jQuery chain. And instead of returning value inside that callback function, now I only pass there value to variable, which is later returned outside of that callback function.

async function A(){     
 let res = 0; 
  await jQuery.get("example.com").done(function(result){ //await was added
    if (result.indexOf("Example")){
      console.log("sucess");
      res = 1; // value is passed to variable, which is returned as result outside this callback function
    } else {
      console.log("Fail");
      res = -1;
    }
  })
  return res; // return statement is outside the callback function
}

function B(){
  A().then(function(result){
    console.log("Res:" + result);
  })
}

B();

I guess the problem was, as @cjl750's answer says, that return statement inside the callback function isn't "seen" as a "true return statement" of that function. So function B() probably saw function A() as a function, which doesn't return anything.


EDIT

I've rewritten the code using $.Deferred() object. It's probably better now. Await is now moved to function B(), it doesn't wait for jQuery chain anymore.

function A(){     
 var dfd = $.Deferred();
 jQuery.get("google.com").then(function(result){ 
    if (result.indexOf("Example")){
      console.log("sucess");
      dfd.resolve(1); 
    } else {
      console.log("Fail");
      dfd.resolve(-1); 
    }
  })
  return dfd.promise();
}

async function B(){
  await A().then(function(result){
    console.log("Res:" + result);
  })
}

B();
Gbr
  • 29
  • 6
  • I was going to come back and update my answer with an example just like this, but you've figured it out already! Nice! – cjl750 Jul 31 '22 at 05:12
  • 3
    Please stop using `.done()`. First off, you should do `let result = await jQuery.get(...);` and not mix `.done()` with `await`. Then, if you must use something like `.done()`, use `.then()` instead. Use the standard promise model that jQuery supports, not jQuery's funky one. See my answer for a simplified approach. – jfriend00 Jul 31 '22 at 06:30
  • 2
    There is NO reason to use a Deferred here - that's an anti-pattern because `jquery.get()` already returns a promise which you can just use. You don't need to create your own Deferred. And, one of the main reasons for making it an anti-pattern is that people usually screw up the error handling which you have messed up here. If `jquery.get()` rejects, you don't handle that at all. – jfriend00 Jul 31 '22 at 08:06