Reputation: 91
I've been having trouble with jQuery.Deferred.prototype.then
,
so I decided to look at jQuery's test suite to check if I
correctly understand the behaviour of this method.
The test which is most relevant to my problem is the following, from
release 3.2.1:
https://github.com/jquery/jquery/blob/3.2.1/test/unit/deferred.js#L133-L168
Note:
Deferred
object is rejected on line 153.done
is called on lines 144 and 164.
I'm not that familiar with QUnit, but it's pretty clear to
me that the test will fail unless
the callbacks that are passed to done
are actually executed.done
should be executed if and only if the Deferred
object
is resolved, not rejected.
(That's my understanding of the Deferred
documentation,
anyway.)But the above 3 points can't all be correct!
Both calls to done
are made on an object returned by
jQuery.Deferred.prototype.then
. I can explain the
code if I posit that, in the Promise
object returned by then
,
the final status is different from the status of the original
Deferred
. However, I can find no hint of this in the jQuery
documentation.
To put my question as briefly as possible: when
the code that I have linked to above is executed, are the callbacks
that are passed to done
executed, and if so, why?
UPDATE
Here is the code that I linked to above (with a few comments added to indicate line numbers):
QUnit.test( "jQuery.Deferred.then - filtering (fail)", function( assert ) {
assert.expect( 4 );
var value1, value2, value3,
defer = jQuery.Deferred(),
piped = defer.then( null, function( a, b ) {
return a * b;
} ),
done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
piped.done( function( result ) { // Line 144
value3 = result;
} );
defer.fail( function( a, b ) {
value1 = a;
value2 = b;
} );
defer.reject( 2, 3 ).then( null, function() { // Line 153
assert.strictEqual( value1, 2, "first reject value ok" );
assert.strictEqual( value2, 3, "second reject value ok" );
assert.strictEqual( value3, 6, "result of filter ok" );
done.pop().call();
} );
jQuery.Deferred().resolve().then( null, function() {
assert.ok( false, "then should not be called on resolve" );
} ).then( done.pop() );
jQuery.Deferred().reject().then( null, jQuery.noop ).done( // Line 164
function( value ) {
assert.strictEqual( value, undefined, "then fail callback can return undefined/null" );
done.pop().call();
} );
} );
UPDATE 2
It turns out that the behaviour of then
changed with the release of jQuery 3 in June 2016. According to a
post
on the jQuery blog, announcing the new release:
The resolution state of a Deferred created by .then() is now controlled by its callbacks—exceptions become rejection values and non-thenable returns become fulfillment values. Previously, returns from rejection handlers became rejection values.
The documentation for then
hasn't been updated yet.
Upvotes: 1
Views: 181
Reputation: 664327
The Deferred object is rejected on line 153.
This is a pretty horrible test, doing many things at once. There are many deferreds and many promises, some of them completely unrelated to each other.
Notice that on line 153, the defer
deferred is rejected, which does have a .fail
callback attached, .then
callbacks attached (which created piped
), and more .then
callbacks attached in line 153 itself.
done is called on lines 144 and 164. I'm not that familiar with QUnit, but it's pretty clear to me that the test will fail unless the callbacks that are passed to done are actually executed.
No. You must not confuse the deferred .done
method and QUnit's done callbacks. In fact, there are three QUnit callbacks stored in the done
array, each of them created by assert.async()
.
The callbacks passed to done should be executed if and only if the Deferred object is resolved, not rejected.
Yes, this is exactly what happens. Notice that the .done
calls in lines 144 and 164 are not made on the rejected defer
deferred, but rather on the piped
promise and another anonymous promise created by .then(null, jQuery.noop)
. Those promises are not rejected, they are fulfilled with the result of the onrejected callback that was passed as the second argument to the respective .then
call.
Upvotes: 2