jQuery deferreds and promises - .then() vs .done()
Solution 1
The callbacks attached to done()
will be fired when the deferred is resolved. The callbacks attached to fail()
will be fired when the deferred is rejected.
Prior to jQuery 1.8, then()
was just syntactic sugar:
promise.then( doneCallback, failCallback )
// was equivalent to
promise.done( doneCallback ).fail( failCallback )
As of 1.8, then()
is an alias for pipe()
and returns a new promise, see here for more information on pipe()
.
success()
and error()
are only available on the jqXHR
object returned by a call to ajax()
. They are simple aliases for done()
and fail()
respectively:
jqXHR.done === jqXHR.success
jqXHR.fail === jqXHR.error
Also, done()
is not limited to a single callback and will filter out non-functions (though there is a bug with strings in version 1.8 that should be fixed in 1.8.1):
// this will add fn1 to 7 to the deferred's internal callback list
// (true, 56 and "omg" will be ignored)
promise.done( fn1, fn2, true, [ fn3, [ fn4, 56, fn5 ], "omg", fn6 ], fn7 );
Same goes for fail()
.
Solution 2
There is also difference in way that return results are processed (its called chaining, done
doesn't chain while then
produces call chains)
promise.then(function (x) { // Suppose promise returns "abc"
console.log(x);
return 123;
}).then(function (x){
console.log(x);
}).then(function (x){
console.log(x)
})
The following results will get logged:
abc
123
undefined
While
promise.done(function (x) { // Suppose promise returns "abc"
console.log(x);
return 123;
}).done(function (x){
console.log(x);
}).done(function (x){
console.log(x)
})
will get the following:
abc
abc
abc
---------- Update:
Btw. I forgot to mention, if you return a Promise instead of atomic type value, the outer promise will wait until inner promise resolves:
promise.then(function (x) { // Suppose promise returns "abc"
console.log(x);
return $http.get('/some/data').then(function (result) {
console.log(result); // suppose result === "xyz"
return result;
});
}).then(function (result){
console.log(result); // result === xyz
}).then(function (und){
console.log(und) // und === undefined, because of absence of return statement in above then
})
in this way it becomes very straightforward to compose parallel or sequential asynchronous operations such as:
// Parallel http requests
promise.then(function (x) { // Suppose promise returns "abc"
console.log(x);
var promise1 = $http.get('/some/data?value=xyz').then(function (result) {
console.log(result); // suppose result === "xyz"
return result;
});
var promise2 = $http.get('/some/data?value=uvm').then(function (result) {
console.log(result); // suppose result === "uvm"
return result;
});
return promise1.then(function (result1) {
return promise2.then(function (result2) {
return { result1: result1, result2: result2; }
});
});
}).then(function (result){
console.log(result); // result === { result1: 'xyz', result2: 'uvm' }
}).then(function (und){
console.log(und) // und === undefined, because of absence of return statement in above then
})
The above code issues two http requests in parallel thus making the requests complete sooner, while below those http requests are being run sequentially thus reducing server load
// Sequential http requests
promise.then(function (x) { // Suppose promise returns "abc"
console.log(x);
return $http.get('/some/data?value=xyz').then(function (result1) {
console.log(result1); // suppose result1 === "xyz"
return $http.get('/some/data?value=uvm').then(function (result2) {
console.log(result2); // suppose result2 === "uvm"
return { result1: result1, result2: result2; };
});
});
}).then(function (result){
console.log(result); // result === { result1: 'xyz', result2: 'uvm' }
}).then(function (und){
console.log(und) // und === undefined, because of absence of return statement in above then
})
Solution 3
.done()
has only one callback and it is the success callback
.then()
has both success and fail callbacks
.fail()
has only one fail callback
so it is up to you what you must do... do you care if it succeeds or if it fails?
Solution 4
deferred.done()
adds handlers to be called only when Deferred is resolved. You can add multiple callbacks to be called.
var url = 'http://jsonplaceholder.typicode.com/posts/1';
$.ajax(url).done(doneCallback);
function doneCallback(result) {
console.log('Result 1 ' + result);
}
You can also write above like this,
function ajaxCall() {
var url = 'http://jsonplaceholder.typicode.com/posts/1';
return $.ajax(url);
}
$.when(ajaxCall()).then(doneCallback, failCallback);
deferred.then()
adds handlers to be called when Deferred is resolved, rejected or still in progress.
var url = 'http://jsonplaceholder.typicode.com/posts/1';
$.ajax(url).then(doneCallback, failCallback);
function doneCallback(result) {
console.log('Result ' + result);
}
function failCallback(result) {
console.log('Result ' + result);
}
Solution 5
There is actually a pretty critical difference, insofar as jQuery's Deferreds are meant to be an implementations of Promises (and jQuery3.0 actually tries to bring them into spec).
The key difference between done/then is that
.done()
ALWAYS returns the same Promise/wrapped values it started with, regardless of what you do or what you return..then()
always returns a NEW Promise, and you are in charge of controlling what that Promise is based on what the function you passed it returned.
Translated from jQuery into native ES2015 Promises, .done()
is sort of like implementing a "tap" structure around a function in a Promise chain, in that it will, if the chain is in the "resolve" state, pass a value to a function... but the result of that function will NOT affect the chain itself.
const doneWrap = fn => x => { fn(x); return x };
Promise.resolve(5)
.then(doneWrap( x => x + 1))
.then(doneWrap(console.log.bind(console)));
$.Deferred().resolve(5)
.done(x => x + 1)
.done(console.log.bind(console));
Those will both log 5, not 6.
Note that I used done and doneWrap to do logging, not .then. That's because console.log functions don't actually return anything. And what happens if you pass .then a function that doesn't return anything?
Promise.resolve(5)
.then(doneWrap( x => x + 1))
.then(console.log.bind(console))
.then(console.log.bind(console));
That will log:
5
undefined
What happened? When I used .then and passed it a function that didn't return anything, it's implicit result was "undefined"... which of course returned a Promise[undefined] to the next then method, which logged undefined. So the original value we started with was basically lost.
.then()
is, at heart, a form of function composition: the result of each step is used as the argument for the function in the next step. That's why .done is best thought of as a "tap"-> it's not actually part of the composition, just something that sneaks a look at the value at a certain step and runs a function at that value, but doesn't actually alter the composition in any way.
This is a pretty fundamental difference, and there's a probably a good reason why native Promises don't have a .done method implemented themselves. We don't eve have to get into why there's no .fail method, because that's even more complicated (namely, .fail/.catch are NOT mirrors of .done/.then -> functions in .catch that return bare values do not "stay" rejected like those passed to .then, they resolve!)
![screenm0nkey](https://i.stack.imgur.com/IWXtC.jpg?s=256&g=1)
screenm0nkey
Updated on July 08, 2022Comments
-
screenm0nkey almost 2 years
I've been reading about jQuery deferreds and promises and I can't see the difference between using
.then()
&.done()
for successful callbacks. I know Eric Hynds mentions that.done()
and.success()
map to the same functionality but I'm guessing so does.then()
as all the callbacks are all invoked on a completion of a successful operation.Can anyone please enlighten me to the correct usage?
-
Shanimal about 11 years+1 for the notion that
done
does nothing to the result wherethen
changes the result. Huge point missed by the others imo. -
bradley.ayers about 11 yearsIt's probably worth mentioning what version of jQuery this applies to, since the behaviour of
then
changed in 1.8 -
oligofren over 10 yearsYou fail to mention that 'then' produces call chains. See Lu4's answer.
-
Michael Kropat over 10 years+1 Straight to the point. I created a runnable example if anyone wants to see what chains with mixed
done
andthen
calls results in. -
Pulak Kanti Bhattacharyya almost 10 yearsthe above example also highlights that 'done' works on original promise object created initially but 'then' returns a new promise.
-
David Harkness almost 10 yearsThis applies to jQuery 1.8+. Older versions act just like the
done
example. Changethen
topipe
in pre-1.8 to get the 1.8+then
behavior. -
Roamer-1888 over 9 yearsCaution! This answer would be correct for several promise implementations but not jQuery, in which
.done()
does not have a terminating role. The documentation says, "Since deferred.done() returns the deferred object, other methods of the deferred object can be chained to this one, including additional .done() methods"..fail()
isn't mentioned but, yes, that could be chained too. -
gleb bahmutov over 9 yearsMy bad, did not check the jQuery
-
wrschneider over 9 years
then
returning a new promise was a key thing I was missing. I couldn't understand why a chain like$.get(....).done(function(data1) { return $.get(...) }).done(function(data2) { ... })
was failing withdata2
undefined; when I changeddone
tothen
it worked, because I was really wanting to pipe promises together rather than attach more handlers to the original promise. -
Andrey over 8 years@glebbahmutov - maybe you should delete this answer so that others don't get confused? Just a friendly suggestion :)
-
Melissa over 8 yearsPlease don't delete the answer, this can help people clear up their misunderstandings too.
-
Flimm almost 8 yearsAlso, jQuery 3.0 is the first version that is compliant with ES2015 and Promises/A+ spec.
-
Flimm almost 8 yearsjQuery 3.0 is the first version that is compliant with Promises/A+ and ES2015 spec.
-
Bob Stein almost 7 years"if you return a Promise ... the outer promise will wait until inner promise resolves" <-- the words I've searched all over for.
-
B M over 6 yearsyour post does not make clear how
then
behaves if nofail
callback is provided - namely not capturing thefail
case at all -
Andy almost 6 yearsPlease can someone review stackoverflow.com/questions/51929634/…. It's been suggested this is a duplicate but the behaviour I'm getting is different to that described above.
-
Mark G. over 5 yearsDon't both
then
anddone
return chainable objects? I feel like the big difference here is that you can modify the return value withthen
and not withdone
. I think both are chainable en.wikipedia.org/wiki/Method_chaining -
Lu4 over 5 years@MarkG. It's that the returned objects are different, I haven't tested this but
done
should return original object which can be further chained, butthen
returns the promise which can also be chained -
CodingYoshi over 5 yearsI still don't understand why I would use one over the other. If I make an ajax call and I need to wait until that call has been fully completed (meaning reponse is returned from server) before I call another ajax call, do I use
done
orthen
? Why? -
David Spector over 5 yearsIn pure JavaScript does a final 'then' leak the memory allocated for the final Promise? If not, what is the use case for 'done'? If 'done' returns the original Promise, then it is useless, since the original Promise is resolved and immutable.
-
David Spector over 5 yearsThe fail case raises an exception that can be caught by the top level of the program. You can also see the exception in the JavaScript console.
-
David Spector over 5 years"then() always means it will be called in whatever case" -- not true. then() is never called in the case of error inside the Promise.
-
Robert Siemer about 4 yearsYour answer is from 2011... Nowadays their return values makes
then()
very different fromdone()
. Asthen()
is often called only with the success callback your point is rather a detail than the main thing to remember/know. (Can’t say how it was before jQuery 3.0.) -
Robert Siemer about 4 yearsInteresting aspect that a
jQuery.Deferred()
can receive multiple values, which it properly passes on to the first.then()
.—A little strange though... as any following.then()
can’t do so. (The chosen interface viareturn
can only return one value.) Javascript’s nativePromise
doesn’t do that. (Which is more consistent, to be honest.) -
Robert Siemer about 4 years@CodingYoshi Check out my answer to finally answer that question (use
.then()
). -
CodingYoshi about 4 yearsA few things: 1) I see what you are saying that
done
will not be executed if a previous done has an exception. But why would it be silently ignored, I mean an exception occurred so why do you say it is silent. 2) I despise theDeferred
object because its API is very very poorly done. It is too complex and confusing. Your code here does not help either to prove your point and it has too much un-needed complexity for what you are trying to prove. 3) Why are thedone
at index 2, 4, and 6 performed before the 2ndthen
? -
CodingYoshi about 4 yearsMy bad, ya definitely deserves a vote. As for your comment about the exception, normally that's how exceptions work: once raised, code after it won't be executed. Plus the jQuery documentation states that it will only be executed if the deferred is resolved.
-
Robert Siemer about 4 years@CodingYoshi The situation is different here: I was only talking about resolved promises/deferreds. I’m not complaining that the rest of the success-handler is not called, that is normal. But I see no reason why a completely different success-handler on a successful promise is not called. All
.then()
will be called, exception (in those handlers) raised or not. But addition/remaining.done()
break. -
Robert Siemer about 4 years@CodingYoshi I greatly improved my answer, if I’m allowed to say. Code and text.
-
Shant Dashjian about 4 yearsThank you. This answer explained the behavior I was seeing. I was using
then()
. My test was failing because the callback was called async, after the test was over. Usingdone()
the callback is called synchronously, satisfying the test expectations, and the test passes. -
Stephan van Hoof over 3 yearsYou can return a thenable from a "resolved/done" handler, and the the promise returned by .then will follow that thenable. So, in the first promise example above, just return $http.get('/some/data?value=xyz'). In other words: do not nest .then inside a .then. Chain them.
-
AFract over 2 yearsInstead of deleting the (wrong) answer, it would be interesting to update it with why it's wrong. And it would avoid downvotes ;)