Skip to content

Commit 35295f1

Browse files
committed
Deferred: Always handle progress callbacks before done/fail
Fixes gh-2013 Fixes gh-2010 Closes gh-2210 (cherry picked from commit 002240a)
1 parent 0de798d commit 35295f1

File tree

2 files changed

+80
-29
lines changed

2 files changed

+80
-29
lines changed

src/deferred.js

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@ jQuery.extend({
1515

1616
Deferred: function( func ) {
1717
var tuples = [
18-
// action, add listener, callbacks, .then handlers, final state
18+
// action, add listener, callbacks,
19+
// ... .then handlers, argument index, [final state]
20+
[ "notify", "progress", jQuery.Callbacks("memory"),
21+
jQuery.Callbacks("memory"), 2 ],
1922
[ "resolve", "done", jQuery.Callbacks("once memory"),
20-
jQuery.Callbacks("once memory"), "resolved" ],
23+
jQuery.Callbacks("once memory"), 0, "resolved" ],
2124
[ "reject", "fail", jQuery.Callbacks("once memory"),
22-
jQuery.Callbacks("once memory"), "rejected" ],
23-
[ "notify", "progress", jQuery.Callbacks("memory"),
24-
jQuery.Callbacks("memory") ]
25+
jQuery.Callbacks("once memory"), 1, "rejected" ]
2526
],
2627
state = "pending",
2728
promise = {
@@ -38,17 +39,19 @@ jQuery.extend({
3839

3940
return jQuery.Deferred(function( newDefer ) {
4041
jQuery.each( tuples, function( i, tuple ) {
41-
var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
42+
// Map tuples (progress, done, fail) to arguments (done, fail, progress)
43+
var fn = jQuery.isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];
44+
45+
// deferred.progress(function() { bind to newDefer or newDefer.notify })
4246
// deferred.done(function() { bind to newDefer or newDefer.resolve })
4347
// deferred.fail(function() { bind to newDefer or newDefer.reject })
44-
// deferred.progress(function() { bind to newDefer or newDefer.notify })
4548
deferred[ tuple[1] ](function() {
4649
var returned = fn && fn.apply( this, arguments );
4750
if ( returned && jQuery.isFunction( returned.promise ) ) {
4851
returned.promise()
52+
.progress( newDefer.notify )
4953
.done( newDefer.resolve )
50-
.fail( newDefer.reject )
51-
.progress( newDefer.notify );
54+
.fail( newDefer.reject );
5255
} else {
5356
newDefer[ tuple[ 0 ] + "With" ](
5457
this === promise ? newDefer.promise() : this,
@@ -176,37 +179,37 @@ jQuery.extend({
176179
}
177180

178181
return jQuery.Deferred(function( newDefer ) {
179-
// fulfilled_handlers.add( ... )
182+
// progress_handlers.add( ... )
180183
tuples[ 0 ][ 3 ].add(
181184
resolve(
182185
0,
183186
newDefer,
184-
jQuery.isFunction( onFulfilled ) ?
185-
onFulfilled :
186-
Identity
187+
jQuery.isFunction( onProgress ) ?
188+
onProgress :
189+
Identity,
190+
newDefer.notifyWith
187191
)
188192
);
189193

190-
// rejected_handlers.add( ... )
194+
// fulfilled_handlers.add( ... )
191195
tuples[ 1 ][ 3 ].add(
192196
resolve(
193197
0,
194198
newDefer,
195-
jQuery.isFunction( onRejected ) ?
196-
onRejected :
197-
Thrower
199+
jQuery.isFunction( onFulfilled ) ?
200+
onFulfilled :
201+
Identity
198202
)
199203
);
200204

201-
// progress_handlers.add( ... )
205+
// rejected_handlers.add( ... )
202206
tuples[ 2 ][ 3 ].add(
203207
resolve(
204208
0,
205209
newDefer,
206-
jQuery.isFunction( onProgress ) ?
207-
onProgress :
208-
Identity,
209-
newDefer.notifyWith
210+
jQuery.isFunction( onRejected ) ?
211+
onRejected :
212+
Thrower
210213
)
211214
);
212215
}).promise();
@@ -222,11 +225,11 @@ jQuery.extend({
222225
// Add list-specific methods
223226
jQuery.each( tuples, function( i, tuple ) {
224227
var list = tuple[ 2 ],
225-
stateString = tuple[ 4 ];
228+
stateString = tuple[ 5 ];
226229

230+
// promise.progress = list.add
227231
// promise.done = list.add
228232
// promise.fail = list.add
229-
// promise.progress = list.add
230233
promise[ tuple[1] ] = list.add;
231234

232235
// Handle state
@@ -240,29 +243,29 @@ jQuery.extend({
240243

241244
// rejected_callbacks.disable
242245
// fulfilled_callbacks.disable
243-
tuples[ i ^ 1 ][ 2 ].disable,
246+
tuples[ 3 - i ][ 2 ].disable,
244247

245248
// progress_callbacks.lock
246-
tuples[ 2 ][ 2 ].lock
249+
tuples[ 0 ][ 2 ].lock
247250
);
248251
}
249252

253+
// progress_handlers.fire
250254
// fulfilled_handlers.fire
251255
// rejected_handlers.fire
252-
// progress_handlers.fire
253256
list.add( tuple[ 3 ].fire );
254257

258+
// deferred.notify = function() { deferred.notifyWith(...) }
255259
// deferred.resolve = function() { deferred.resolveWith(...) }
256260
// deferred.reject = function() { deferred.rejectWith(...) }
257-
// deferred.notify = function() { deferred.notifyWith(...) }
258261
deferred[ tuple[0] ] = function() {
259262
deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
260263
return this;
261264
};
262265

266+
// deferred.notifyWith = list.fireWith
263267
// deferred.resolveWith = list.fireWith
264268
// deferred.rejectWith = list.fireWith
265-
// deferred.notifyWith = list.fireWith
266269
deferred[ tuple[0] + "With" ] = list.fireWith;
267270
});
268271

test/unit/deferred.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,54 @@ test( "jQuery.Deferred.then - progress and thenables", function( assert ) {
565565
trigger.notify();
566566
});
567567

568+
test( "jQuery.Deferred - notify and resolve", function( assert ) {
569+
570+
expect( 7 );
571+
572+
var notifiedResolved = jQuery.Deferred().notify( "foo" )/*xxx .resolve( "bar" )*/,
573+
done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
574+
575+
notifiedResolved.progress( function( v ) {
576+
assert.strictEqual( v, "foo", "progress value" );
577+
} );
578+
579+
notifiedResolved.pipe().progress( function( v ) {
580+
assert.strictEqual( v, "foo", "piped progress value" );
581+
} );
582+
583+
notifiedResolved.pipe( null, null, function() {
584+
return "baz";
585+
} ).progress( function( v ) {
586+
assert.strictEqual( v, "baz", "replaced piped progress value" );
587+
} );
588+
589+
notifiedResolved.pipe( null, null, function() {
590+
return jQuery.Deferred().notify( "baz" ).resolve( "quux" );
591+
} ).progress( function( v ) {
592+
assert.strictEqual( v, "baz", "deferred replaced piped progress value" );
593+
} );
594+
595+
notifiedResolved.then().progress( function( v ) {
596+
assert.strictEqual( v, "foo", "then'd progress value" );
597+
done.pop().call();
598+
} );
599+
600+
notifiedResolved.then( null, null, function() {
601+
return "baz";
602+
} ).progress( function( v ) {
603+
assert.strictEqual( v, "baz", "replaced then'd progress value" );
604+
done.pop().call();
605+
} );
606+
607+
notifiedResolved.then( null, null, function() {
608+
return jQuery.Deferred().notify( "baz" ).resolve( "quux" );
609+
} ).progress( function( v ) {
610+
// Progress from the surrogate deferred is ignored
611+
assert.strictEqual( v, "quux", "deferred replaced then'd progress value" );
612+
done.pop().call();
613+
} );
614+
});
615+
568616
test( "jQuery.when", function() {
569617

570618
expect( 37 );

0 commit comments

Comments
 (0)