diff --git a/src/deferred.js b/src/deferred.js index 8139515fef..f2b7d2bc68 100644 --- a/src/deferred.js +++ b/src/deferred.js @@ -45,6 +45,9 @@ function adoptValue( value, resolve, reject ) { } } +var processQueue = []; +var processQueueTimeout; + jQuery.extend( { Deferred: function( func ) { @@ -183,8 +186,13 @@ jQuery.extend( { }, // Only normal processors (resolve) catch and reject exceptions + // Exceptions in other cases should not effect the promise result process = special ? - mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) {} + } : function() { try { mightThrow(); @@ -212,12 +220,14 @@ jQuery.extend( { } }; + processQueue.push( process ); + // Support: Promises/A+ section 2.3.3.3.1 // https://promisesaplus.com/#point-57 // Re-resolve promises immediately to dodge false rejection from // subsequent errors if ( depth ) { - process(); + deferredTick(); } else { // Call an optional hook to record the stack, in case of exception @@ -225,7 +235,11 @@ jQuery.extend( { if ( jQuery.Deferred.getStackHook ) { process.stackTrace = jQuery.Deferred.getStackHook(); } - window.setTimeout( process ); + + // Ensure processing has been scheduled + if ( !processQueueTimeout ) { + processQueueTimeout = window.setTimeout( deferredTick ); + } } }; } @@ -385,5 +399,12 @@ jQuery.extend( { } } ); +function deferredTick() { + processQueueTimeout = undefined; + jQuery.each( processQueue.splice( 0, processQueue.length ), function( _, process ) { + process(); + } ); +} + return jQuery; } ); diff --git a/test/unit/deferred.js b/test/unit/deferred.js index 32f3256814..466c88e5ad 100644 --- a/test/unit/deferred.js +++ b/test/unit/deferred.js @@ -372,6 +372,28 @@ QUnit.test( "jQuery.Deferred.then - deferred (progress)", function( assert ) { } ); } ); +QUnit.test( "jQuery.Deferred.then - deferred (progress) exceptions", function( assert ) { + + assert.expect( 1 ); + + var piped1, piped2, + defer = jQuery.Deferred(), + done = assert.async(); + + piped1 = defer.then( null, null, function() { + throw new Error( "You cant see me" ); + } ); + + piped2 = defer.then( null, null, function( n ) { + assert.strictEqual( n, 42, "Errors in previous notifiers don't stop other notify callbacks" ); + } ); + + piped2.then( done ); + + defer.notify( 42 ); + defer.resolve(); +} ); + QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - deferred (progress)", function( assert ) { assert.expect( 3 );