mightThrow();
} catch ( e ) {
+ if ( jQuery.Deferred.exceptionHook ) {
+ jQuery.Deferred.exceptionHook( e,
+ process.stackTrace );
+ }
+
// Support: Promises/A+ section 2.3.3.3.4.1
// https://promisesaplus.com/#point-61
// Ignore post-resolution exceptions
if ( depth + 1 >= maxDepth ) {
- // Only substitue handlers pass on context
+ // Only substitute handlers pass on context
// and multiple values (non-spec behavior)
if ( handler !== Thrower ) {
that = undefined;
if ( depth ) {
process();
} else {
+
+ // Call an optional hook to record the stack, in case of exception
+ // since it's otherwise lost when execution goes async
+ if ( jQuery.Deferred.getStackHook ) {
+ process.stackTrace = jQuery.Deferred.getStackHook();
+ }
window.setTimeout( process );
}
};
--- /dev/null
+define( [
+ "../core",
+ "../deferred"
+], function( jQuery ) {
+
+// These usually indicate a programmer mistake during development,
+// warn about them ASAP rather than swallowing them by default.
+var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;
+
+jQuery.Deferred.exceptionHook = function( error, stack ) {
+
+ // Support: IE9
+ // Console exists when dev tools are open, which can happen at any time
+ if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {
+ window.console.warn( "jQuery.Deferred exception: " + error.message, stack );
+ }
+};
+
+} );
} catch ( _ ) {}
} );
+QUnit[ window.console ? "test" : "skip" ]( "jQuery.Deferred.exceptionHook", function( assert ) {
+
+ assert.expect( 1 );
+
+ var done = assert.async(),
+ defer = jQuery.Deferred(),
+ oldWarn = window.console.warn;
+
+ window.console.warn = function( msg ) {
+ assert.ok( /barf/.test( msg ), "Message: " + msg );
+ };
+ jQuery.when(
+ defer.then( function() {
+ // Should get an error
+ jQuery.barf();
+ } ).then( null, jQuery.noop ),
+ defer.then( function() {
+ // Should NOT get an error
+ throw new Error( "Make me a sandwich" );
+ } ).then( null, jQuery.noop )
+ ).then( function( ) {
+ window.console.warn = oldWarn;
+ done();
+ } );
+
+ defer.resolve();
+} );
+
+QUnit[ window.console ? "test" : "skip" ]( "jQuery.Deferred.exceptionHook with stack hooks", function( assert ) {
+
+ assert.expect( 2 );
+
+ var done = assert.async(),
+ defer = jQuery.Deferred(),
+ oldWarn = window.console.warn;
+
+ jQuery.Deferred.getStackHook = function() {
+ // Default exceptionHook assumes the stack is in a form console.warn can log,
+ // but a custom getStackHook+exceptionHook pair could save a raw form and
+ // format it to a string only when an exception actually occurs.
+ // For the unit test we just ensure the plumbing works.
+ return "NO STACK FOR YOU";
+ };
+
+ window.console.warn = function( msg, stack ) {
+ assert.ok( /cough_up_hairball/.test( msg ), "Function mentioned: " + msg );
+ assert.ok( /NO STACK FOR YOU/.test( stack ), "Stack trace included: " + stack );
+ };
+ defer.then( function() {
+ jQuery.cough_up_hairball();
+ } ).then( null, function( ) {
+ window.console.warn = oldWarn;
+ delete jQuery.Deferred.getStackHook;
+ done();
+ } );
+
+ defer.resolve();
+} );
+
QUnit.test( "jQuery.Deferred - 1.x/2.x compatibility", function( assert ) {
assert.expect( 8 );