aboutsummaryrefslogtreecommitdiffstats
path: root/src/deferred.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/deferred.js')
-rw-r--r--src/deferred.js100
1 files changed, 55 insertions, 45 deletions
diff --git a/src/deferred.js b/src/deferred.js
index 0ea1e7f1f..6e4d43b31 100644
--- a/src/deferred.js
+++ b/src/deferred.js
@@ -13,6 +13,38 @@ function Thrower( ex ) {
throw ex;
}
+function adoptValue( value, resolve, reject ) {
+ var method;
+
+ try {
+
+ // Check for promise aspect first to privilege synchronous behavior
+ if ( value && jQuery.isFunction( ( method = value.promise ) ) ) {
+ method.call( value ).done( resolve ).fail( reject );
+
+ // Other thenables
+ } else if ( value && jQuery.isFunction( ( method = value.then ) ) ) {
+ method.call( value, resolve, reject );
+
+ // Other non-thenables
+ } else {
+
+ // Support: Android 4.0 only
+ // Strict mode functions invoked without .call/.apply get global-object context
+ resolve.call( undefined, value );
+ }
+
+ // For Promises/A+, convert exceptions into rejections
+ // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in
+ // Deferred#then to conditionally suppress rejection.
+ } catch ( /*jshint -W002 */ value ) {
+
+ // Support: Android 4.0 only
+ // Strict mode functions invoked without .call/.apply get global-object context
+ reject.call( undefined, value );
+ }
+}
+
jQuery.extend( {
Deferred: function( func ) {
@@ -305,67 +337,45 @@ jQuery.extend( {
},
// Deferred helper
- when: function() {
- var method, resolveContexts,
- i = 0,
- resolveValues = slice.call( arguments ),
- length = resolveValues.length,
+ when: function( singleValue ) {
+ var
+
+ // count of uncompleted subordinates
+ remaining = arguments.length,
- // the count of uncompleted subordinates
- remaining = length,
+ // count of unprocessed arguments
+ i = remaining,
+
+ // subordinate fulfillment data
+ resolveContexts = Array( i ),
+ resolveValues = slice.call( arguments ),
- // the master Deferred.
+ // the master Deferred
master = jQuery.Deferred(),
- // Update function for both resolving subordinates
+ // subordinate callback factory
updateFunc = function( i ) {
return function( value ) {
resolveContexts[ i ] = this;
resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
if ( !( --remaining ) ) {
- master.resolveWith(
- resolveContexts.length === 1 ? resolveContexts[ 0 ] : resolveContexts,
- resolveValues
- );
+ master.resolveWith( resolveContexts, resolveValues );
}
};
};
- // Add listeners to promise-like subordinates; treat others as resolved
- if ( length > 0 ) {
- resolveContexts = new Array( length );
- for ( ; i < length; i++ ) {
-
- // jQuery.Deferred - treated specially to get resolve-sync behavior
- if ( resolveValues[ i ] &&
- jQuery.isFunction( ( method = resolveValues[ i ].promise ) ) ) {
-
- method.call( resolveValues[ i ] )
- .done( updateFunc( i ) )
- .fail( master.reject );
-
- // Other thenables
- } else if ( resolveValues[ i ] &&
- jQuery.isFunction( ( method = resolveValues[ i ].then ) ) ) {
-
- method.call(
- resolveValues[ i ],
- updateFunc( i ),
- master.reject
- );
- } else {
-
- // Support: Android 4.0 only
- // Strict mode functions invoked without .call/.apply get global-object context
- updateFunc( i ).call( undefined, resolveValues[ i ] );
- }
- }
+ // Single- and empty arguments are adopted like Promise.resolve
+ if ( remaining <= 1 ) {
+ adoptValue( singleValue, master.resolve, master.reject );
- // If we're not waiting on anything, resolve the master
- } else {
- master.resolveWith();
+ // Use .then() to unwrap secondary thenables (cf. gh-3000)
+ return master.then();
}
+ // Multiple arguments are aggregated like Promise.all array elements
+ while ( i-- ) {
+ adoptValue( resolveValues[ i ], updateFunc( i ), master.reject );
+ }
return master.promise();
}
} );