Skip to content

Commit 78b9eac

Browse files
committed
Deferred: syncronize single and multiple target handling in $.when
Fixes jquerygh-2546 Fixes jquerygh-2018 Close jquerygh-2707
1 parent 8b65446 commit 78b9eac

File tree

2 files changed

+46
-18
lines changed

2 files changed

+46
-18
lines changed

src/deferred.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -294,19 +294,17 @@ jQuery.extend( {
294294
},
295295

296296
// Deferred helper
297-
when: function( subordinate /* , ..., subordinateN */ ) {
297+
when: function() {
298298
var method,
299299
i = 0,
300300
resolveValues = slice.call( arguments ),
301301
length = resolveValues.length,
302302

303303
// the count of uncompleted subordinates
304-
remaining = length !== 1 ||
305-
( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
304+
remaining = length,
306305

307306
// the master Deferred.
308-
// If resolveValues consist of only a single Deferred, just use that.
309-
master = remaining === 1 ? subordinate : jQuery.Deferred(),
307+
master = jQuery.Deferred(),
310308

311309
// Update function for both resolve and progress values
312310
updateFunc = function( i, contexts, values ) {
@@ -316,14 +314,17 @@ jQuery.extend( {
316314
if ( values === progressValues ) {
317315
master.notifyWith( contexts, values );
318316
} else if ( !( --remaining ) ) {
319-
master.resolveWith( contexts, values );
317+
master.resolveWith(
318+
contexts.length === 1 ? contexts[ 0 ] : contexts,
319+
values
320+
);
320321
}
321322
};
322323
},
323324
progressValues, progressContexts, resolveContexts;
324325

325326
// Add listeners to Deferred subordinates; treat others as resolved
326-
if ( length > 1 ) {
327+
if ( length > 0 ) {
327328
progressValues = new Array( length );
328329
progressContexts = new Array( length );
329330
resolveContexts = new Array( length );
@@ -345,14 +346,13 @@ jQuery.extend( {
345346
updateFunc( i, progressContexts, progressValues )
346347
);
347348
} else {
348-
--remaining;
349+
updateFunc( i, resolveContexts, resolveValues )( resolveValues[ i ] );
349350
}
350351
}
351-
}
352352

353353
// If we're not waiting on anything, resolve the master
354-
if ( !remaining ) {
355-
master.resolveWith( resolveContexts, resolveValues );
354+
} else {
355+
master.resolveWith();
356356
}
357357

358358
return master.promise();

test/unit/deferred.js

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,6 @@ QUnit.test( "jQuery.when", function( assert ) {
667667
"undefined": undefined,
668668
"a plain object": {},
669669
"an array": [ 1, 2, 3 ]
670-
671670
}, function( message, value ) {
672671
assert.ok(
673672
jQuery.isFunction(
@@ -698,12 +697,10 @@ QUnit.test( "jQuery.when", function( assert ) {
698697
} );
699698

700699
jQuery.each( [ 1, 2, 3 ], function( k, i ) {
701-
702700
jQuery.when( cache || jQuery.Deferred( function() {
703701
this.resolve( i );
704702
} )
705703
).done( function( value ) {
706-
707704
assert.strictEqual( value, 1, "Function executed" + ( i > 1 ? " only once" : "" ) );
708705
cache = value;
709706
} );
@@ -759,10 +756,8 @@ QUnit.test( "jQuery.when - joined", function( assert ) {
759756
expected = shouldResolve ? [ 1, 1 ] : [ 0, undefined ],
760757
expectedNotify = shouldNotify && [ willNotify[ id1 ], willNotify[ id2 ] ],
761758
code = "jQuery.when( " + id1 + ", " + id2 + " )",
762-
context1 = defer1 && jQuery.isFunction( defer1.promise ) ? defer1.promise() :
763-
( defer1.then ? window : undefined ),
764-
context2 = defer2 && jQuery.isFunction( defer2.promise ) ? defer2.promise() :
765-
( defer2.then ? window : undefined );
759+
context1 = defer1 && jQuery.isFunction( defer1.promise ) ? defer1.promise() : window,
760+
context2 = defer2 && jQuery.isFunction( defer2.promise ) ? defer2.promise() : window;
766761

767762
jQuery.when( defer1, defer2 ).done( function( a, b ) {
768763
if ( shouldResolve ) {
@@ -880,3 +875,36 @@ QUnit.test( "jQuery.when - chaining", function( assert ) {
880875

881876
defer.resolve( "other deferred" );
882877
} );
878+
879+
QUnit.test( "jQuery.when - solitary thenables", function( assert ) {
880+
881+
assert.expect( 1 );
882+
883+
var done = assert.async(),
884+
rejected = new Promise( function( resolve, reject ) {
885+
setTimeout( function() {
886+
reject( "rejected" );
887+
}, 100 );
888+
} );
889+
890+
jQuery.when( rejected ).then(
891+
function() {
892+
assert.ok( false, "Rejected, solitary, non-Deferred thenable should not resolve" );
893+
done();
894+
},
895+
function() {
896+
assert.ok( true, "Rejected, solitary, non-Deferred thenable rejected properly" );
897+
done();
898+
}
899+
);
900+
} );
901+
902+
QUnit.test( "jQuery.when does not reuse a solitary jQuery Deferred (gh-2018)", function( assert ) {
903+
904+
assert.expect( 2 );
905+
var defer = jQuery.Deferred().resolve(),
906+
promise = jQuery.when( defer );
907+
908+
assert.equal( promise.state(), "resolved", "Master Deferred is immediately resolved" );
909+
assert.notStrictEqual( defer.promise(), promise, "jQuery.when returns the master deferred's promise" );
910+
} );

0 commit comments

Comments
 (0)