From 78b9eac1198627eb4dad0cc35334c7704449f310 Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Wed, 11 Nov 2015 10:35:37 -0500 Subject: [PATCH] Deferred: syncronize single and multiple target handling in $.when Fixes gh-2546 Fixes gh-2018 Close gh-2707 --- src/deferred.js | 22 +++++++++++----------- test/unit/deferred.js | 42 +++++++++++++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/src/deferred.js b/src/deferred.js index e1af425d4..846528e79 100644 --- a/src/deferred.js +++ b/src/deferred.js @@ -294,19 +294,17 @@ jQuery.extend( { }, // Deferred helper - when: function( subordinate /* , ..., subordinateN */ ) { + when: function() { var method, i = 0, resolveValues = slice.call( arguments ), length = resolveValues.length, // the count of uncompleted subordinates - remaining = length !== 1 || - ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + remaining = length, // the master Deferred. - // If resolveValues consist of only a single Deferred, just use that. - master = remaining === 1 ? subordinate : jQuery.Deferred(), + master = jQuery.Deferred(), // Update function for both resolve and progress values updateFunc = function( i, contexts, values ) { @@ -316,14 +314,17 @@ jQuery.extend( { if ( values === progressValues ) { master.notifyWith( contexts, values ); } else if ( !( --remaining ) ) { - master.resolveWith( contexts, values ); + master.resolveWith( + contexts.length === 1 ? contexts[ 0 ] : contexts, + values + ); } }; }, progressValues, progressContexts, resolveContexts; // Add listeners to Deferred subordinates; treat others as resolved - if ( length > 1 ) { + if ( length > 0 ) { progressValues = new Array( length ); progressContexts = new Array( length ); resolveContexts = new Array( length ); @@ -345,14 +346,13 @@ jQuery.extend( { updateFunc( i, progressContexts, progressValues ) ); } else { - --remaining; + updateFunc( i, resolveContexts, resolveValues )( resolveValues[ i ] ); } } - } // If we're not waiting on anything, resolve the master - if ( !remaining ) { - master.resolveWith( resolveContexts, resolveValues ); + } else { + master.resolveWith(); } return master.promise(); diff --git a/test/unit/deferred.js b/test/unit/deferred.js index 2277df190..1a721d1ee 100644 --- a/test/unit/deferred.js +++ b/test/unit/deferred.js @@ -667,7 +667,6 @@ QUnit.test( "jQuery.when", function( assert ) { "undefined": undefined, "a plain object": {}, "an array": [ 1, 2, 3 ] - }, function( message, value ) { assert.ok( jQuery.isFunction( @@ -698,12 +697,10 @@ QUnit.test( "jQuery.when", function( assert ) { } ); jQuery.each( [ 1, 2, 3 ], function( k, i ) { - jQuery.when( cache || jQuery.Deferred( function() { this.resolve( i ); } ) ).done( function( value ) { - assert.strictEqual( value, 1, "Function executed" + ( i > 1 ? " only once" : "" ) ); cache = value; } ); @@ -759,10 +756,8 @@ QUnit.test( "jQuery.when - joined", function( assert ) { expected = shouldResolve ? [ 1, 1 ] : [ 0, undefined ], expectedNotify = shouldNotify && [ willNotify[ id1 ], willNotify[ id2 ] ], code = "jQuery.when( " + id1 + ", " + id2 + " )", - context1 = defer1 && jQuery.isFunction( defer1.promise ) ? defer1.promise() : - ( defer1.then ? window : undefined ), - context2 = defer2 && jQuery.isFunction( defer2.promise ) ? defer2.promise() : - ( defer2.then ? window : undefined ); + context1 = defer1 && jQuery.isFunction( defer1.promise ) ? defer1.promise() : window, + context2 = defer2 && jQuery.isFunction( defer2.promise ) ? defer2.promise() : window; jQuery.when( defer1, defer2 ).done( function( a, b ) { if ( shouldResolve ) { @@ -880,3 +875,36 @@ QUnit.test( "jQuery.when - chaining", function( assert ) { defer.resolve( "other deferred" ); } ); + +QUnit.test( "jQuery.when - solitary thenables", function( assert ) { + + assert.expect( 1 ); + + var done = assert.async(), + rejected = new Promise( function( resolve, reject ) { + setTimeout( function() { + reject( "rejected" ); + }, 100 ); + } ); + + jQuery.when( rejected ).then( + function() { + assert.ok( false, "Rejected, solitary, non-Deferred thenable should not resolve" ); + done(); + }, + function() { + assert.ok( true, "Rejected, solitary, non-Deferred thenable rejected properly" ); + done(); + } + ); +} ); + +QUnit.test( "jQuery.when does not reuse a solitary jQuery Deferred (gh-2018)", function( assert ) { + + assert.expect( 2 ); + var defer = jQuery.Deferred().resolve(), + promise = jQuery.when( defer ); + + assert.equal( promise.state(), "resolved", "Master Deferred is immediately resolved" ); + assert.notStrictEqual( defer.promise(), promise, "jQuery.when returns the master deferred's promise" ); +} ); -- 2.39.5