user1678025 user1678025 - 5 months ago 17
Ajax Question

jQuery .done not returning as expected

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" ?

Answer

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