Skip to content

Commit 7608437

Browse files
committed
Deferred: Remove default callback context
Employs strict mode to simplify Deferred callback context handling. Fixes gh-3060 Closes gh-3061
1 parent 8442759 commit 7608437

File tree

4 files changed

+57
-38
lines changed

4 files changed

+57
-38
lines changed

build/tasks/build.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ module.exports = function( grunt ) {
2323
baseUrl: "src",
2424
name: "jquery",
2525

26+
// Allow strict mode
27+
useStrict: true,
28+
2629
// We have multiple minify steps
2730
optimize: "none",
2831

src/deferred.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ define( [
44
"./callbacks"
55
], function( jQuery, slice ) {
66

7+
"use strict";
8+
79
function Identity( v ) {
810
return v;
911
}
@@ -60,7 +62,7 @@ jQuery.extend( {
6062
.fail( newDefer.reject );
6163
} else {
6264
newDefer[ tuple[ 0 ] + "With" ](
63-
this === promise ? newDefer.promise() : this,
65+
this,
6466
fn ? [ returned ] : arguments
6567
);
6668
}
@@ -73,7 +75,7 @@ jQuery.extend( {
7375
var maxDepth = 0;
7476
function resolve( depth, deferred, handler, special ) {
7577
return function() {
76-
var that = this === promise ? undefined : this,
78+
var that = this,
7779
args = arguments,
7880
mightThrow = function() {
7981
var returned, then;
@@ -144,8 +146,7 @@ jQuery.extend( {
144146

145147
// Process the value(s)
146148
// Default process is resolve
147-
( special || deferred.resolveWith )(
148-
that || deferred.promise(), args );
149+
( special || deferred.resolveWith )( that, args );
149150
}
150151
},
151152

@@ -174,8 +175,7 @@ jQuery.extend( {
174175
args = [ e ];
175176
}
176177

177-
deferred.rejectWith( that || deferred.promise(),
178-
args );
178+
deferred.rejectWith( that, args );
179179
}
180180
}
181181
};
@@ -282,7 +282,7 @@ jQuery.extend( {
282282
// deferred.resolve = function() { deferred.resolveWith(...) }
283283
// deferred.reject = function() { deferred.rejectWith(...) }
284284
deferred[ tuple[ 0 ] ] = function() {
285-
deferred[ tuple[ 0 ] + "With" ]( this === deferred ? promise : this, arguments );
285+
deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
286286
return this;
287287
};
288288

src/wrapper.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@
3838
// Pass this if window is not defined yet
3939
}( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
4040

41-
// Support: Firefox 18+
42-
// Can't be in strict mode, several libs including ASP.NET trace
43-
// the stack via arguments.caller.callee and Firefox dies if
44-
// you try to trace through "use strict" call chains. (#13335)
45-
//"use strict";
41+
// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
42+
// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
43+
// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common
44+
// enough that all such attempts are guarded in a try block.
45+
"use strict";
4646

4747
// @CODE
4848
// build.js inserts compiled jQuery here

test/unit/deferred.js

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,21 @@ jQuery.each( [ "", " - new operator" ], function( _, withNew ) {
1616

1717
assert.ok( jQuery.isFunction( defer.pipe ), "defer.pipe is a function" );
1818

19-
createDeferred().resolve().done( function() {
19+
defer.resolve().done( function() {
2020
assert.ok( true, "Success on resolve" );
21-
assert.strictEqual( this.state(), "resolved", "Deferred is resolved (state)" );
21+
assert.strictEqual( defer.state(), "resolved", "Deferred is resolved (state)" );
2222
} ).fail( function() {
2323
assert.ok( false, "Error on resolve" );
2424
} ).always( function() {
2525
assert.ok( true, "Always callback on resolve" );
2626
} );
2727

28-
createDeferred().reject().done( function() {
28+
defer = createDeferred();
29+
defer.reject().done( function() {
2930
assert.ok( false, "Success on reject" );
3031
} ).fail( function() {
3132
assert.ok( true, "Error on reject" );
32-
assert.strictEqual( this.state(), "rejected", "Deferred is rejected (state)" );
33+
assert.strictEqual( defer.state(), "rejected", "Deferred is rejected (state)" );
3334
} ).always( function() {
3435
assert.ok( true, "Always callback on reject" );
3536
} );
@@ -405,21 +406,31 @@ QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - deferred (progress)", function(
405406

406407
QUnit.test( "jQuery.Deferred.then - context", function( assert ) {
407408

408-
assert.expect( 7 );
409+
assert.expect( 11 );
409410

410411
var defer, piped, defer2, piped2,
411-
context = {},
412-
done = jQuery.map( new Array( 4 ), function() { return assert.async(); } );
412+
context = { custom: true },
413+
done = jQuery.map( new Array( 5 ), function() { return assert.async(); } );
413414

414415
jQuery.Deferred().resolveWith( context, [ 2 ] ).then( function( value ) {
416+
assert.strictEqual( this, context, "custom context received by .then handler" );
415417
return value * 3;
416418
} ).done( function( value ) {
417-
assert.notStrictEqual( this, context, "custom context not propagated through .then" );
419+
assert.notStrictEqual( this, context,
420+
"custom context not propagated through .then handler" );
418421
assert.strictEqual( value, 6, "proper value received" );
419422
done.pop().call();
420423
} );
421424

425+
jQuery.Deferred().resolveWith( context, [ 2 ] ).then().done( function( value ) {
426+
assert.strictEqual( this, context,
427+
"custom context propagated through .then without handler" );
428+
assert.strictEqual( value, 2, "proper value received" );
429+
done.pop().call();
430+
} );
431+
422432
jQuery.Deferred().resolve().then( function() {
433+
assert.strictEqual( this, window, "default context in .then handler" );
423434
return jQuery.Deferred().resolveWith( context );
424435
} ).done( function() {
425436
assert.strictEqual( this, context,
@@ -435,8 +446,7 @@ QUnit.test( "jQuery.Deferred.then - context", function( assert ) {
435446
defer.resolve( 2 );
436447

437448
piped.done( function( value ) {
438-
assert.strictEqual( this, piped,
439-
"default context gets updated to latest promise in the chain" );
449+
assert.strictEqual( this, window, ".then handler does not introduce context" );
440450
assert.strictEqual( value, 6, "proper value received" );
441451
done.pop().call();
442452
} );
@@ -447,30 +457,39 @@ QUnit.test( "jQuery.Deferred.then - context", function( assert ) {
447457
defer2.resolve( 2 );
448458

449459
piped2.done( function( value ) {
450-
assert.strictEqual( this, piped2,
451-
"default context updated to latest promise in the chain (without passing function)" );
460+
assert.strictEqual( this, window, ".then without handler does not introduce context" );
452461
assert.strictEqual( value, 2, "proper value received (without passing function)" );
453462
done.pop().call();
454463
} );
455464
} );
456465

457466
QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - context", function( assert ) {
458467

459-
assert.expect( 7 );
468+
assert.expect( 11 );
460469

461470
var defer, piped, defer2, piped2,
462-
context = {},
463-
done = jQuery.map( new Array( 4 ), function() { return assert.async(); } );
471+
context = { custom: true },
472+
done = jQuery.map( new Array( 5 ), function() { return assert.async(); } );
464473

465474
jQuery.Deferred().resolveWith( context, [ 2 ] ).pipe( function( value ) {
475+
assert.strictEqual( this, context, "custom context received by .pipe handler" );
466476
return value * 3;
467477
} ).done( function( value ) {
468-
assert.strictEqual( this, context, "[PIPE ONLY] custom context correctly propagated" );
478+
assert.strictEqual( this, context,
479+
"[PIPE ONLY] custom context propagated through .pipe handler" );
469480
assert.strictEqual( value, 6, "proper value received" );
470481
done.pop().call();
471482
} );
472483

484+
jQuery.Deferred().resolveWith( context, [ 2 ] ).pipe().done( function( value ) {
485+
assert.strictEqual( this, context,
486+
"[PIPE ONLY] custom context propagated through .pipe without handler" );
487+
assert.strictEqual( value, 2, "proper value received" );
488+
done.pop().call();
489+
} );
490+
473491
jQuery.Deferred().resolve().pipe( function() {
492+
assert.strictEqual( this, window, "default context in .pipe handler" );
474493
return jQuery.Deferred().resolveWith( context );
475494
} ).done( function() {
476495
assert.strictEqual( this, context,
@@ -486,8 +505,7 @@ QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - context", function( assert ) {
486505
defer.resolve( 2 );
487506

488507
piped.done( function( value ) {
489-
assert.strictEqual( this, piped,
490-
"default context gets updated to latest promise in the chain" );
508+
assert.strictEqual( this, window, ".pipe handler does not introduce context" );
491509
assert.strictEqual( value, 6, "proper value received" );
492510
done.pop().call();
493511
} );
@@ -498,8 +516,7 @@ QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - context", function( assert ) {
498516
defer2.resolve( 2 );
499517

500518
piped2.done( function( value ) {
501-
assert.strictEqual( this, piped2,
502-
"default context updated to latest promise in the chain (without passing function)" );
519+
assert.strictEqual( this, window, ".pipe without handler does not introduce context" );
503520
assert.strictEqual( value, 2, "proper value received (without passing function)" );
504521
done.pop().call();
505522
} );
@@ -825,7 +842,8 @@ QUnit.test( "jQuery.when - joined", function( assert ) {
825842
eventuallyRejected: true,
826843
rejectedStandardPromise: true
827844
},
828-
counter = 49;
845+
counter = 49,
846+
expectedContext = (function() { "use strict"; return this; })();
829847

830848
QUnit.stop();
831849

@@ -840,15 +858,13 @@ QUnit.test( "jQuery.when - joined", function( assert ) {
840858
var shouldResolve = willSucceed[ id1 ] && willSucceed[ id2 ],
841859
shouldError = willError[ id1 ] || willError[ id2 ],
842860
expected = shouldResolve ? [ 1, 1 ] : [ 0, undefined ],
843-
code = "jQuery.when( " + id1 + ", " + id2 + " )",
844-
context1 = defer1 && jQuery.isFunction( defer1.promise ) ? defer1.promise() : window,
845-
context2 = defer2 && jQuery.isFunction( defer2.promise ) ? defer2.promise() : window;
861+
code = "jQuery.when( " + id1 + ", " + id2 + " )";
846862

847863
jQuery.when( defer1, defer2 ).done( function( a, b ) {
848864
if ( shouldResolve ) {
849865
assert.deepEqual( [ a, b ], expected, code + " => resolve" );
850-
assert.strictEqual( this[ 0 ], context1, code + " => first context OK" );
851-
assert.strictEqual( this[ 1 ], context2, code + " => second context OK" );
866+
assert.strictEqual( this[ 0 ], expectedContext, code + " => context[0] OK" );
867+
assert.strictEqual( this[ 1 ], expectedContext, code + " => context[1] OK" );
852868
} else {
853869
assert.ok( false, code + " => resolve" );
854870
}

0 commit comments

Comments
 (0)