Skip to content

Commit de71e97

Browse files
committed
Deferred: Make jQuery.when synchronous when possible
Closes gh-3102 Fixes gh-3100 Closes gh-3105
1 parent e8825a5 commit de71e97

File tree

2 files changed

+47
-8
lines changed

2 files changed

+47
-8
lines changed

src/deferred.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -366,16 +366,21 @@ jQuery.extend( {
366366

367367
// Single- and empty arguments are adopted like Promise.resolve
368368
if ( remaining <= 1 ) {
369-
adoptValue( singleValue, master.resolve, master.reject );
369+
adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject );
370370

371371
// Use .then() to unwrap secondary thenables (cf. gh-3000)
372-
return master.then();
372+
if ( master.state() === "pending" ||
373+
jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {
374+
375+
return master.then();
376+
}
373377
}
374378

375379
// Multiple arguments are aggregated like Promise.all array elements
376380
while ( i-- ) {
377381
adoptValue( resolveValues[ i ], updateFunc( i ), master.reject );
378382
}
383+
379384
return master.promise();
380385
}
381386
} );

test/unit/deferred.js

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -824,24 +824,27 @@ QUnit.test( "jQuery.when(nonThenable) - like Promise.resolve", function( assert
824824
QUnit.test( "jQuery.when(thenable) - like Promise.resolve", function( assert ) {
825825
"use strict";
826826

827-
assert.expect( 56 );
828-
829-
var slice = [].slice,
827+
var CASES = 16,
828+
slice = [].slice,
830829
sentinel = { context: "explicit" },
831830
eventuallyFulfilled = jQuery.Deferred().notify( true ),
832831
eventuallyRejected = jQuery.Deferred().notify( true ),
832+
secondaryFulfilled = jQuery.Deferred().resolve( eventuallyFulfilled ),
833+
secondaryRejected = jQuery.Deferred().resolve( eventuallyRejected ),
833834
inputs = {
834835
promise: Promise.resolve( true ),
835836
rejectedPromise: Promise.reject( false ),
836837
deferred: jQuery.Deferred().resolve( true ),
837838
eventuallyFulfilled: eventuallyFulfilled,
838-
secondaryFulfilled: jQuery.Deferred().resolve( eventuallyFulfilled ),
839+
secondaryFulfilled: secondaryFulfilled,
840+
eventuallySecondaryFulfilled: jQuery.Deferred().notify( true ),
839841
multiDeferred: jQuery.Deferred().resolve( "foo", "bar" ),
840842
deferredWith: jQuery.Deferred().resolveWith( sentinel, [ true ] ),
841843
multiDeferredWith: jQuery.Deferred().resolveWith( sentinel, [ "foo", "bar" ] ),
842844
rejectedDeferred: jQuery.Deferred().reject( false ),
843845
eventuallyRejected: eventuallyRejected,
844-
secondaryRejected: jQuery.Deferred().resolve( eventuallyRejected ),
846+
secondaryRejected: secondaryRejected,
847+
eventuallySecondaryRejected: jQuery.Deferred().notify( true ),
845848
multiRejectedDeferred: jQuery.Deferred().reject( "baz", "quux" ),
846849
rejectedDeferredWith: jQuery.Deferred().rejectWith( sentinel, [ false ] ),
847850
multiRejectedDeferredWith: jQuery.Deferred().rejectWith( sentinel, [ "baz", "quux" ] )
@@ -857,6 +860,7 @@ QUnit.test( "jQuery.when(thenable) - like Promise.resolve", function( assert ) {
857860
deferred: [ true ],
858861
eventuallyFulfilled: [ true ],
859862
secondaryFulfilled: [ true ],
863+
eventuallySecondaryFulfilled: [ true ],
860864
multiDeferred: [ "foo", "bar" ],
861865
deferredWith: [ true ],
862866
multiDeferredWith: [ "foo", "bar" ]
@@ -866,6 +870,7 @@ QUnit.test( "jQuery.when(thenable) - like Promise.resolve", function( assert ) {
866870
rejectedDeferred: [ false ],
867871
eventuallyRejected: [ false ],
868872
secondaryRejected: [ false ],
873+
eventuallySecondaryRejected: [ false ],
869874
multiRejectedDeferred: [ "baz", "quux" ],
870875
rejectedDeferredWith: [ false ],
871876
multiRejectedDeferredWith: [ "baz", "quux" ]
@@ -875,7 +880,9 @@ QUnit.test( "jQuery.when(thenable) - like Promise.resolve", function( assert ) {
875880
// Strict mode functions invoked without .call/.apply get global-object context
876881
defaultContext = (function getDefaultContext() { return this; }).call(),
877882

878-
done = assert.async( 28 );
883+
done = assert.async( CASES * 2 );
884+
885+
assert.expect( CASES * 4 );
879886

880887
jQuery.each( inputs, function( message, value ) {
881888
var code = "jQuery.when( " + message + " )",
@@ -917,6 +924,8 @@ QUnit.test( "jQuery.when(thenable) - like Promise.resolve", function( assert ) {
917924
setTimeout( function() {
918925
eventuallyFulfilled.resolve( true );
919926
eventuallyRejected.reject( false );
927+
inputs.eventuallySecondaryFulfilled.resolve( secondaryFulfilled );
928+
inputs.eventuallySecondaryRejected.resolve( secondaryRejected );
920929
}, 50 );
921930
} );
922931

@@ -1049,3 +1058,28 @@ QUnit.test( "jQuery.when - notify does not affect resolved", function( assert )
10491058
assert.ok( false, "Error on resolve" );
10501059
} );
10511060
} );
1061+
1062+
QUnit.test( "jQuery.when(...) - opportunistically synchronous", function( assert ) {
1063+
1064+
assert.expect( 5 );
1065+
1066+
var when = "before",
1067+
resolved = jQuery.Deferred().resolve( true ),
1068+
rejected = jQuery.Deferred().reject( false ),
1069+
validate = function( label ) {
1070+
return function() {
1071+
assert.equal( when, "before", label );
1072+
};
1073+
},
1074+
done = assert.async( 5 );
1075+
1076+
jQuery.when().done( validate( "jQuery.when()" ) ).always( done );
1077+
jQuery.when( when ).done( validate( "jQuery.when(nonThenable)" ) ).always( done );
1078+
jQuery.when( resolved ).done( validate( "jQuery.when(alreadyFulfilled)" ) ).always( done );
1079+
jQuery.when( rejected ).fail( validate( "jQuery.when(alreadyRejected)" ) ).always( done );
1080+
jQuery.when( resolved, rejected )
1081+
.always( validate( "jQuery.when(alreadyFulfilled, alreadyRejected)" ) )
1082+
.always( done );
1083+
1084+
when = "after";
1085+
} );

0 commit comments

Comments
 (0)