Skip to content

Commit

Permalink
Deferred: Always handle progress callbacks before done/fail
Browse files Browse the repository at this point in the history
Fixes gh-2013
Fixes gh-2010
Closes gh-2210
  • Loading branch information
gibson042 committed Apr 22, 2015
1 parent 55ac56a commit 002240a
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 29 deletions.
61 changes: 32 additions & 29 deletions src/deferred.js
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ jQuery.extend({


Deferred: function( func ) { Deferred: function( func ) {
var tuples = [ var tuples = [
// action, add listener, callbacks, .then handlers, final state // action, add listener, callbacks,
// ... .then handlers, argument index, [final state]
[ "notify", "progress", jQuery.Callbacks("memory"),
jQuery.Callbacks("memory"), 2 ],
[ "resolve", "done", jQuery.Callbacks("once memory"), [ "resolve", "done", jQuery.Callbacks("once memory"),
jQuery.Callbacks("once memory"), "resolved" ], jQuery.Callbacks("once memory"), 0, "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), [ "reject", "fail", jQuery.Callbacks("once memory"),
jQuery.Callbacks("once memory"), "rejected" ], jQuery.Callbacks("once memory"), 1, "rejected" ]
[ "notify", "progress", jQuery.Callbacks("memory"),
jQuery.Callbacks("memory") ]
], ],
state = "pending", state = "pending",
promise = { promise = {
Expand All @@ -38,17 +39,19 @@ jQuery.extend({


return jQuery.Deferred(function( newDefer ) { return jQuery.Deferred(function( newDefer ) {
jQuery.each( tuples, function( i, tuple ) { jQuery.each( tuples, function( i, tuple ) {
var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; // Map tuples (progress, done, fail) to arguments (done, fail, progress)
var fn = jQuery.isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];

// deferred.progress(function() { bind to newDefer or newDefer.notify })
// deferred.done(function() { bind to newDefer or newDefer.resolve }) // deferred.done(function() { bind to newDefer or newDefer.resolve })
// deferred.fail(function() { bind to newDefer or newDefer.reject }) // deferred.fail(function() { bind to newDefer or newDefer.reject })
// deferred.progress(function() { bind to newDefer or newDefer.notify })
deferred[ tuple[1] ](function() { deferred[ tuple[1] ](function() {
var returned = fn && fn.apply( this, arguments ); var returned = fn && fn.apply( this, arguments );
if ( returned && jQuery.isFunction( returned.promise ) ) { if ( returned && jQuery.isFunction( returned.promise ) ) {
returned.promise() returned.promise()
.progress( newDefer.notify )
.done( newDefer.resolve ) .done( newDefer.resolve )
.fail( newDefer.reject ) .fail( newDefer.reject );
.progress( newDefer.notify );
} else { } else {
newDefer[ tuple[ 0 ] + "With" ]( newDefer[ tuple[ 0 ] + "With" ](
this === promise ? newDefer.promise() : this, this === promise ? newDefer.promise() : this,
Expand Down Expand Up @@ -176,37 +179,37 @@ jQuery.extend({
} }


return jQuery.Deferred(function( newDefer ) { return jQuery.Deferred(function( newDefer ) {
// fulfilled_handlers.add( ... ) // progress_handlers.add( ... )
tuples[ 0 ][ 3 ].add( tuples[ 0 ][ 3 ].add(
resolve( resolve(
0, 0,
newDefer, newDefer,
jQuery.isFunction( onFulfilled ) ? jQuery.isFunction( onProgress ) ?
onFulfilled : onProgress :
Identity Identity,
newDefer.notifyWith
) )
); );


// rejected_handlers.add( ... ) // fulfilled_handlers.add( ... )
tuples[ 1 ][ 3 ].add( tuples[ 1 ][ 3 ].add(
resolve( resolve(
0, 0,
newDefer, newDefer,
jQuery.isFunction( onRejected ) ? jQuery.isFunction( onFulfilled ) ?
onRejected : onFulfilled :
Thrower Identity
) )
); );


// progress_handlers.add( ... ) // rejected_handlers.add( ... )
tuples[ 2 ][ 3 ].add( tuples[ 2 ][ 3 ].add(
resolve( resolve(
0, 0,
newDefer, newDefer,
jQuery.isFunction( onProgress ) ? jQuery.isFunction( onRejected ) ?
onProgress : onRejected :
Identity, Thrower
newDefer.notifyWith
) )
); );
}).promise(); }).promise();
Expand All @@ -222,11 +225,11 @@ jQuery.extend({
// Add list-specific methods // Add list-specific methods
jQuery.each( tuples, function( i, tuple ) { jQuery.each( tuples, function( i, tuple ) {
var list = tuple[ 2 ], var list = tuple[ 2 ],
stateString = tuple[ 4 ]; stateString = tuple[ 5 ];


// promise.progress = list.add
// promise.done = list.add // promise.done = list.add
// promise.fail = list.add // promise.fail = list.add
// promise.progress = list.add
promise[ tuple[1] ] = list.add; promise[ tuple[1] ] = list.add;


// Handle state // Handle state
Expand All @@ -240,29 +243,29 @@ jQuery.extend({


// rejected_callbacks.disable // rejected_callbacks.disable
// fulfilled_callbacks.disable // fulfilled_callbacks.disable
tuples[ i ^ 1 ][ 2 ].disable, tuples[ 3 - i ][ 2 ].disable,


// progress_callbacks.lock // progress_callbacks.lock
tuples[ 2 ][ 2 ].lock tuples[ 0 ][ 2 ].lock
); );
} }


// progress_handlers.fire
// fulfilled_handlers.fire // fulfilled_handlers.fire
// rejected_handlers.fire // rejected_handlers.fire
// progress_handlers.fire
list.add( tuple[ 3 ].fire ); list.add( tuple[ 3 ].fire );


// deferred.notify = function() { deferred.notifyWith(...) }
// deferred.resolve = function() { deferred.resolveWith(...) } // deferred.resolve = function() { deferred.resolveWith(...) }
// deferred.reject = function() { deferred.rejectWith(...) } // deferred.reject = function() { deferred.rejectWith(...) }
// deferred.notify = function() { deferred.notifyWith(...) }
deferred[ tuple[0] ] = function() { deferred[ tuple[0] ] = function() {
deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
return this; return this;
}; };


// deferred.notifyWith = list.fireWith
// deferred.resolveWith = list.fireWith // deferred.resolveWith = list.fireWith
// deferred.rejectWith = list.fireWith // deferred.rejectWith = list.fireWith
// deferred.notifyWith = list.fireWith
deferred[ tuple[0] + "With" ] = list.fireWith; deferred[ tuple[0] + "With" ] = list.fireWith;
}); });


Expand Down
48 changes: 48 additions & 0 deletions test/unit/deferred.js
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -565,6 +565,54 @@ test( "jQuery.Deferred.then - progress and thenables", function( assert ) {
trigger.notify(); trigger.notify();
}); });


test( "jQuery.Deferred - notify and resolve", function( assert ) {

expect( 7 );

var notifiedResolved = jQuery.Deferred().notify( "foo" )/*xxx .resolve( "bar" )*/,
done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );

notifiedResolved.progress( function( v ) {
assert.strictEqual( v, "foo", "progress value" );
} );

notifiedResolved.pipe().progress( function( v ) {
assert.strictEqual( v, "foo", "piped progress value" );
} );

notifiedResolved.pipe( null, null, function() {
return "baz";
} ).progress( function( v ) {
assert.strictEqual( v, "baz", "replaced piped progress value" );
} );

notifiedResolved.pipe( null, null, function() {
return jQuery.Deferred().notify( "baz" ).resolve( "quux" );
} ).progress( function( v ) {
assert.strictEqual( v, "baz", "deferred replaced piped progress value" );
} );

notifiedResolved.then().progress( function( v ) {
assert.strictEqual( v, "foo", "then'd progress value" );
done.pop().call();
} );

notifiedResolved.then( null, null, function() {
return "baz";
} ).progress( function( v ) {
assert.strictEqual( v, "baz", "replaced then'd progress value" );
done.pop().call();
} );

notifiedResolved.then( null, null, function() {
return jQuery.Deferred().notify( "baz" ).resolve( "quux" );
} ).progress( function( v ) {
// Progress from the surrogate deferred is ignored
assert.strictEqual( v, "quux", "deferred replaced then'd progress value" );
done.pop().call();
} );
});

test( "jQuery.when", function() { test( "jQuery.when", function() {


expect( 37 ); expect( 37 );
Expand Down

0 comments on commit 002240a

Please sign in to comment.