jQuery's Deferred
has two functions which can be used to implement asynchronous chaining of functions:
then()
deferred.then( doneCallbacks, failCallbacks ) Returns: Deferred
doneCallbacks A function, or array of functions, called when the Deferred is resolved.
failCallbacks A function, or array of functions, called when the Deferred is rejected.
pipe()
deferred.pipe( [doneFilter] [, failFilter] ) Returns: Promise
doneFilter An optional function that is called when the Deferred is resolved.
failFilter An optional function that is called when the Deferred is rejected.
I know then()
has been around a little longer than pipe()
so the latter must add some extra benefit, but what the difference precisely is eludes me. Both take pretty much the same callback parameters though they differ in name and the difference between returning a Deferred
and returning a Promise
seems slight.
I've read the official docs over and over but always find them too "dense" to really wrap my head around and searching has found lots of discussion of the one feature or the other but I haven't found anything that really clarifies the different pros and cons of each.
So when is it better to use then
and when is it better to use pipe
?
Addition
Felix's excellent answer has really helped clarify how these two functions differ. But I wonder if there are times when the functionality of then()
is preferable to that of pipe()
.
It is apparent that pipe()
is more powerful than then()
and it seems the former can do anything the latter can do. One reason to use then()
might be that its name reflects its role as the termination of a chain of functions processing the same data.
But is there a use case that requires then()
's returning the original Deferred
that can't be done with pipe()
due to it returning a new Promise
?
In fact it turns out that the difference between
.then()
and.pipe()
has been deemed unnecessary and they have been made to be the same as of jQuery version 1.8.From a comment by
jaubourg
in jQuery's bug tracker ticket #11010 "MAKE DEFERRED.THEN == DEFERRED.PIPE LIKE PROMISE/A":(emphassis mine)
There is no case where you MUST use
then()
overpipe()
. You can always choose to ignore the value thatpipe()
will pass in. There might be a slight performance hit for usingpipe
-- but it is unlikely to matter.So it might seem like you could simply always use
pipe()
in both cases. However, by usingpipe()
, you are communicating to other people reading your code (including yourself, six months from now) that there is some importance to the return value. If you're discarding it, you're violating this semantic construct.It's like having a function that returns a value that is never used: confusing.
So use
then()
when you should, andpipe()
when you should...Since jQuery 1.8
.then
behaves the same as.pipe
:and
The examples below might still be helpful to some.
They serve different purposes:
.then()
is to be used whenever you want to work with the result of the process, i.e. as the documentation says, when the deferred object is resolved or rejected. It is the same as using.done()
or.fail()
.You'd use
.pipe()
to (pre)filter the result somehow. The return value of a callback to.pipe()
will be passed as argument to thedone
andfail
callbacks. It can also return another deferred object and the following callbacks will be registered on this deferred.That is not the case with
.then()
(or.done()
,.fail()
), the return values of the registered callbacks are just ignored.So it is not that you use either
.then()
or.pipe()
. You could use.pipe()
for the same purposes as.then()
but the converse does not hold.Example 1
The result of some operation is an array of objects:
and you want to compute the minimum and maximum of the values. Lets assume we use two
done
callbacks:In both cases you have to iterate over the list and extract the value from each object.
Wouldn't it be better to somehow extract the values beforehand so that you don't have to do this in both callbacks individually? Yes! And that's what we can use
.pipe()
for:Obviously this is a made up example and there are many different (maybe better) ways to solve this problem, but I hope it illustrates the point.
Example 2
Consider Ajax calls. Sometimes you want to initiate one Ajax call after a previous one completes. One way is to make the second call inside a
done
callback:Now lets assume you want to decouple your code and put these two Ajax calls inside a function:
You'd like to use the deferred object to allow other code which calls
makeCalls
to attach callbacks for the second Ajax call, butwould not have the desired effect as the second call is made inside a
done
callback and not accessible from the outside.The solution would be to use
.pipe()
instead:By using
.pipe()
you can now make it possible to append callbacks to the "inner" Ajax call without exposing the actual flow/order of the calls.In general, deferred objects provide an interesting way to decouple your code :)