From f93a2f569d31c4d1fc86ff3ae9605309ac491d68 Mon Sep 17 00:00:00 2001 From: jaubourg Date: Sat, 12 May 2012 21:41:39 +0200 Subject: [PATCH] Preserves context objects when multiple Deferreds are passed to $.when(). Context is an array containing the original contexts in order. When non-observable value is given, associated context is undefined. In case only a single non-observable value is given, context is the global object (thanks so much Function.prototype.apply!). Fixes #11749. --- src/deferred.js | 28 ++++++++++++++++------------ test/unit/deferred.js | 26 ++++++++++++++++++++------ 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/deferred.js b/src/deferred.js index a06fe2193..6c42583a5 100644 --- a/src/deferred.js +++ b/src/deferred.js @@ -97,35 +97,39 @@ jQuery.extend({ var i = 0, resolveValues = sliceDeferred.call( arguments ), length = resolveValues.length, - progressValues = new Array( length ), // the count of uncompleted subordinates remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, // the master Deferred. If resolveValues consist of only a single Deferred, just use that. deferred = remaining === 1 ? subordinate : jQuery.Deferred(), - promise = deferred.promise(), // Update function for both resolve and progress values - updateFunc = function( i, arr ) { + updateFunc = function( i, contexts, values ) { return function( value ) { - arr[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments ) : value; - if( arr === progressValues ) { - deferred.notifyWith( promise, arr ); + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments ) : value; + if( values === progressValues ) { + deferred.notifyWith( contexts, values ); } else if ( !( --remaining ) ) { - deferred.resolveWith( promise, arr ); + deferred.resolveWith( contexts, values ); } }; - }; + }, + + progressValues, progressContexts, resolveContexts; // add listeners to Deferred subordinates; treat others as resolved if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); for ( ; i < length; i++ ) { if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { resolveValues[ i ].promise() - .done( updateFunc( i, resolveValues ) ) + .done( updateFunc( i, resolveContexts, resolveValues ) ) .fail( deferred.reject ) - .progress( updateFunc( i, progressValues ) ); + .progress( updateFunc( i, progressContexts, progressValues ) ); } else { --remaining; } @@ -134,10 +138,10 @@ jQuery.extend({ // if we're not waiting on anything, resolve the master if ( !remaining ) { - deferred.resolveWith( deferred, resolveValues ); + deferred.resolveWith( resolveContexts, resolveValues ); } - return promise; + return deferred.promise(); } }); diff --git a/test/unit/deferred.js b/test/unit/deferred.js index 710d221f0..973560724 100644 --- a/test/unit/deferred.js +++ b/test/unit/deferred.js @@ -289,7 +289,7 @@ test( "jQuery.Deferred.then - context", function() { test( "jQuery.when" , function() { - expect( 23 ); + expect( 34 ); // Some other objects jQuery.each( { @@ -307,15 +307,23 @@ test( "jQuery.when" , function() { } , function( message , value ) { ok( jQuery.isFunction( jQuery.when( value ).done(function( resolveValue ) { + strictEqual( this, window, "Context is the global object with " + message ); strictEqual( resolveValue , value , "Test the promise was resolved with " + message ); }).promise ) , "Test " + message + " triggers the creation of a new Promise" ); } ); ok( jQuery.isFunction( jQuery.when().done(function( resolveValue ) { - strictEqual( resolveValue , undefined , "Test the promise was resolved with no parameter" ); + strictEqual( this, window, "Test the promise was resolved with window as its context" ); + strictEqual( resolveValue, undefined, "Test the promise was resolved with no parameter" ); }).promise ) , "Test calling when with no parameter triggers the creation of a new Promise" ); + var context = {}; + + jQuery.when( jQuery.Deferred().resolveWith( context ) ).done(function() { + strictEqual( this, context, "when( promise ) propagates context" ); + }); + var cache, i; for( i = 1 ; i < 4 ; i++ ) { @@ -330,7 +338,7 @@ test( "jQuery.when" , function() { test("jQuery.when - joined", function() { - expect(53); + expect( 119 ); var deferreds = { value: 1, @@ -362,11 +370,15 @@ test("jQuery.when - joined", function() { shouldNotify = willNotify[ id1 ] || willNotify[ id2 ], expected = shouldResolve ? [ 1, 1 ] : [ 0, undefined ], expectedNotify = shouldNotify && [ willNotify[ id1 ], willNotify[ id2 ] ], - code = id1 + "/" + id2; + code = id1 + "/" + id2, + context1 = defer1 && jQuery.isFunction( defer1.promise ) ? defer1 : undefined, + context2 = defer2 && jQuery.isFunction( defer2.promise ) ? defer2 : undefined; - var promise = jQuery.when( defer1, defer2 ).done(function( a, b ) { + jQuery.when( defer1, defer2 ).done(function( a, b ) { if ( shouldResolve ) { deepEqual( [ a, b ], expected, code + " => resolve" ); + strictEqual( this[ 0 ], context1, code + " => first context OK" ); + strictEqual( this[ 1 ], context2, code + " => second context OK" ); } else { ok( false , code + " => resolve" ); } @@ -376,8 +388,10 @@ test("jQuery.when - joined", function() { } else { ok( false , code + " => reject" ); } - }).progress(function progress( a, b ) { + }).progress(function( a, b ) { deepEqual( [ a, b ], expectedNotify, code + " => progress" ); + strictEqual( this[ 0 ], expectedNotify[ 0 ] ? context1 : undefined, code + " => first context OK" ); + strictEqual( this[ 1 ], expectedNotify[ 1 ] ? context2 : undefined, code + " => second context OK" ); }); } ); } ); -- 2.39.5