0

I know this is a misunderstanding on my part and i'm trying to learn what i'm doing wrong and I could really use some help please.

I have an ajax request that is succeeding and returning data just fine. The Api returns an error code, then I try to return a response based on what code I was given, except that the function is only returning what the ajax call delivered.

function getData(){
    var promise = $.ajax({
       url: 'api.php',
       type: 'POST',
       dataType: 'json'
    });
    return promise.done(function(data){
        console.log(data);
        if(data.errorCode === 0){
            return data;
        } else {
            return 'failed';
        }
    });
}
$(document).ready(function(){
   $('.btn').click(function(){
       var apiData = getData();
       console.log(apiData);
   });
});

So if the api returns an error code that is not 0 then the function getData should return a string of 'failed', except it returns the data element. I know that the code is not 0 because the console.log shows that the code is 999. What am I doing wrong? Why I can't I get it to return my string of "failed" ?

user1678025
  • 23
  • 1
  • 6
  • 2
    Post how you use getData. – dfsq Jun 20 '16 at 16:36
  • 1
    What dfsq said. In particular, if you're doing `if (getData() == "failed")` and expecting that to indicate that it failed, that's the problem. `getData` returns a *promise* (the jQuery flavor), not `data` or `"failed"`. – T.J. Crowder Jun 20 '16 at 16:39
  • 2
    You can never directly return from an async call. Promises aren't some magic that makes that any less true. The caller of `getData()` is not going to have access to the result immediately. Best you can do is resume your logic from *within* a callback. Whether you use Promises or not, that's the case. –  Jun 20 '16 at 16:39
  • Updated the code to show how I'm invoking getData() function. – user1678025 Jun 20 '16 at 16:47
  • TJ, so if I understand this right. The first return in the if statement is my string, then the return of the promise.done() overwrites the first return and changes it to the data that .done() was delivered. so it's kind of doing this; return = 'failed'; return = data; – user1678025 Jun 20 '16 at 16:51
  • You can return the `promise` var and then in `getData()` function handle the `done()` method – Marcos Pérez Gude Jun 20 '16 at 16:52
  • @user1678025: I've added some further explanation of promises to my answer. See also: http://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call?lq=1 and http://stackoverflow.com/questions/18422021/how-do-promises-work-in-javascript. – T.J. Crowder Jun 20 '16 at 17:03

2 Answers2

4

getData doesn't (and can't) return the data or "failed"; it returns a promise. You consume that promise much the way you did inside getData:

$(document).ready(function(){
   $('.btn').click(function(){
       getData().done(function(apiData) {   // **
           console.log(apiData);            // **
       });                                  // **
   });
});

In that callback, apiData will be whatever your callback in getData returned, so it'll be the data object or the string "failed".

(I've used done there because it's what you used elsewhere, but normally I'd use then, the standard promise function.)

One of the key things to understand about promises is that then (and done) returns a new promise (technically, with real promises, a thenable) that will be settled based on what the callback does.

So consider (let's stick with jQuery's Deferred for now):

function doSomething() {
  var d = $.Deferred();
  setTimeout(function() {
    // Resolve our promise with "a"
    d.resolve("a");
  }, 10);
  return d.promise();
}

// Consume the promise and put it through a chain:
doSomething()
  .then(function(result) {
    // This callback happens to do synchronous processing
    console.log("First callback got", result);
    return result.toUpperCase();
  })
  .then(function(result) {
    // This one does something async, and so it returns a promise
    var cd = $.Deferred();
    setTimeout(function() {
      console.log("Second callback got", result);
      cd.resolve(result + result);
    }, 10);
    return cd.promise();
  })
  .then(function(result) {
    console.log("Third callback got", result);
  });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

The output of that is

First callback got a
Second callback got A
Third callback got AA

Now, earlier I said then always returns a promise (thenable). How does it do that when my first callback is synchronous and returns a value directly, but my second one is async and returns a promise? The then function looks at the return value from the callback and, if it's "thenable" (something with then on it), returns it; if it's not thenable, it creates a new, resolved promise with the value as the resolution value.


Just for completeness, here's the example above with native JavaScript promises (requires browser support):

function doSomething() {
  return new Promise(function(resolve) {
    setTimeout(function() {
      // Resolve our promise with "a"
      resolve("a");
    }, 10);
  });
}

// Consume the promise and put it through a chain:
doSomething()
  .then(function(result) {
    // This callback happens to do synchronous processing
    console.log("First callback got", result);
    return result.toUpperCase();
  })
  .then(function(result) {
    // This one does something async, and so it returns a promise
    return new Promise(function(resolve) {
      setTimeout(function() {
        console.log("Second callback got", result);
        resolve(result + result);
      }, 10);
    });
  })
  .then(function(result) {
    console.log("Third callback got", result);
  });

The output of that is

First callback got a
Second callback got A
Third callback got AA
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
0

According to http://api.jquery.com/jquery.ajax/ documentation, you can either chain .done, .fail, .always or .then.

$(document).ready(function() {
  $('.btn').click(function() {
    $.ajax({
       url: 'api.php',
       type: 'POST',
       dataType: 'json'
    })
    .done(function(data) {
        console.log(data);
        if (data.errorCode === 0) {
            console.log(data)
        } else {
            console.log(data)
        }
    });
  });
});

This will work

Akinjide
  • 2,723
  • 22
  • 28