]> source.dussan.org Git - jquery.git/commitdiff
Deferred: Make jQuery.when synchronous when possible
authorRichard Gibson <richard.gibson@gmail.com>
Wed, 4 May 2016 19:30:24 +0000 (15:30 -0400)
committerRichard Gibson <richard.gibson@gmail.com>
Mon, 9 May 2016 16:14:22 +0000 (12:14 -0400)
Closes gh-3102
Fixes gh-3100
Closes gh-3105

src/deferred.js
test/unit/deferred.js

index 6e4d43b315ac0489161dbd26d3972fbd822564d9..73b2f9ef2a9258df9ab2ae5b5bfffe46890ab174 100644 (file)
@@ -366,16 +366,21 @@ jQuery.extend( {
 
                // Single- and empty arguments are adopted like Promise.resolve
                if ( remaining <= 1 ) {
-                       adoptValue( singleValue, master.resolve, master.reject );
+                       adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject );
 
                        // Use .then() to unwrap secondary thenables (cf. gh-3000)
-                       return master.then();
+                       if ( master.state() === "pending" ||
+                               jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {
+
+                               return master.then();
+                       }
                }
 
                // Multiple arguments are aggregated like Promise.all array elements
                while ( i-- ) {
                        adoptValue( resolveValues[ i ], updateFunc( i ), master.reject );
                }
+
                return master.promise();
        }
 } );
index 830103eeb5d2cf978ded62367985955544e80d96..9065553197fa0cb0ef2e08b31a3812257f2e883a 100644 (file)
@@ -824,24 +824,27 @@ QUnit.test( "jQuery.when(nonThenable) - like Promise.resolve", function( assert
 QUnit.test( "jQuery.when(thenable) - like Promise.resolve", function( assert ) {
        "use strict";
 
-       assert.expect( 56 );
-
-       var slice = [].slice,
+       var CASES = 16,
+               slice = [].slice,
                sentinel = { context: "explicit" },
                eventuallyFulfilled = jQuery.Deferred().notify( true ),
                eventuallyRejected = jQuery.Deferred().notify( true ),
+               secondaryFulfilled = jQuery.Deferred().resolve( eventuallyFulfilled ),
+               secondaryRejected = jQuery.Deferred().resolve( eventuallyRejected ),
                inputs = {
                        promise: Promise.resolve( true ),
                        rejectedPromise: Promise.reject( false ),
                        deferred: jQuery.Deferred().resolve( true ),
                        eventuallyFulfilled: eventuallyFulfilled,
-                       secondaryFulfilled: jQuery.Deferred().resolve( eventuallyFulfilled ),
+                       secondaryFulfilled: secondaryFulfilled,
+                       eventuallySecondaryFulfilled: jQuery.Deferred().notify( true ),
                        multiDeferred: jQuery.Deferred().resolve( "foo", "bar" ),
                        deferredWith: jQuery.Deferred().resolveWith( sentinel, [ true ] ),
                        multiDeferredWith: jQuery.Deferred().resolveWith( sentinel, [ "foo", "bar" ] ),
                        rejectedDeferred: jQuery.Deferred().reject( false ),
                        eventuallyRejected: eventuallyRejected,
-                       secondaryRejected: jQuery.Deferred().resolve( eventuallyRejected ),
+                       secondaryRejected: secondaryRejected,
+                       eventuallySecondaryRejected: jQuery.Deferred().notify( true ),
                        multiRejectedDeferred: jQuery.Deferred().reject( "baz", "quux" ),
                        rejectedDeferredWith: jQuery.Deferred().rejectWith( sentinel, [ false ] ),
                        multiRejectedDeferredWith: jQuery.Deferred().rejectWith( sentinel, [ "baz", "quux" ] )
@@ -857,6 +860,7 @@ QUnit.test( "jQuery.when(thenable) - like Promise.resolve", function( assert ) {
                        deferred: [ true ],
                        eventuallyFulfilled: [ true ],
                        secondaryFulfilled: [ true ],
+                       eventuallySecondaryFulfilled: [ true ],
                        multiDeferred: [ "foo", "bar" ],
                        deferredWith: [ true ],
                        multiDeferredWith: [ "foo", "bar" ]
@@ -866,6 +870,7 @@ QUnit.test( "jQuery.when(thenable) - like Promise.resolve", function( assert ) {
                        rejectedDeferred: [ false ],
                        eventuallyRejected: [ false ],
                        secondaryRejected: [ false ],
+                       eventuallySecondaryRejected: [ false ],
                        multiRejectedDeferred: [ "baz", "quux" ],
                        rejectedDeferredWith: [ false ],
                        multiRejectedDeferredWith: [ "baz", "quux" ]
@@ -875,7 +880,9 @@ QUnit.test( "jQuery.when(thenable) - like Promise.resolve", function( assert ) {
                // Strict mode functions invoked without .call/.apply get global-object context
                defaultContext = (function getDefaultContext() { return this; }).call(),
 
-               done = assert.async( 28 );
+               done = assert.async( CASES * 2 );
+
+       assert.expect( CASES * 4 );
 
        jQuery.each( inputs, function( message, value ) {
                var code = "jQuery.when( " + message + " )",
@@ -917,6 +924,8 @@ QUnit.test( "jQuery.when(thenable) - like Promise.resolve", function( assert ) {
        setTimeout( function() {
                eventuallyFulfilled.resolve( true );
                eventuallyRejected.reject( false );
+               inputs.eventuallySecondaryFulfilled.resolve( secondaryFulfilled );
+               inputs.eventuallySecondaryRejected.resolve( secondaryRejected );
        }, 50 );
 } );
 
@@ -1049,3 +1058,28 @@ QUnit.test( "jQuery.when - notify does not affect resolved", function( assert )
                assert.ok( false, "Error on resolve" );
        } );
 } );
+
+QUnit.test( "jQuery.when(...) - opportunistically synchronous", function( assert ) {
+
+       assert.expect( 5 );
+
+       var when = "before",
+               resolved = jQuery.Deferred().resolve( true ),
+               rejected = jQuery.Deferred().reject( false ),
+               validate = function( label ) {
+                       return function() {
+                               assert.equal( when, "before", label );
+                       };
+               },
+               done = assert.async( 5 );
+
+       jQuery.when().done( validate( "jQuery.when()" ) ).always( done );
+       jQuery.when( when ).done( validate( "jQuery.when(nonThenable)" ) ).always( done );
+       jQuery.when( resolved ).done( validate( "jQuery.when(alreadyFulfilled)" ) ).always( done );
+       jQuery.when( rejected ).fail( validate( "jQuery.when(alreadyRejected)" ) ).always( done );
+       jQuery.when( resolved, rejected )
+               .always( validate( "jQuery.when(alreadyFulfilled, alreadyRejected)" ) )
+               .always( done );
+
+       when = "after";
+} );