From cca7118658a074771fb3598145e78ca39b93c20d Mon Sep 17 00:00:00 2001 From: Michał Gołębiowski-Owczarek Date: Tue, 14 Mar 2023 22:32:57 +0100 Subject: Deferred: Rename `getStackHook` to `getErrorHook` (3.x version) Rename `jQuery.Deferred.getStackHook` to `jQuery.Deferred.getErrorHook` to indicate passing an error instance is usually a better choice - it works with source maps while a raw stack generally does not. In jQuery `3.7.0`, we'll keep both names, marking the old one as deprecated. In jQuery `4.0.0` we'll just keep the new one. This change implements the `3.7.0` version; PR gh-5211 implements the `4.0.0` one. Fixes gh-5201 Closes gh-5212 Ref gh-5211 --- src/deferred.js | 15 +++++++++++---- src/deferred/exceptionHook.js | 8 ++++++-- test/unit/deferred.js | 10 +++++----- test/unit/deprecated.js | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/deferred.js b/src/deferred.js index 439653f58..5e390b7f6 100644 --- a/src/deferred.js +++ b/src/deferred.js @@ -194,7 +194,7 @@ jQuery.extend( { if ( jQuery.Deferred.exceptionHook ) { jQuery.Deferred.exceptionHook( e, - process.stackTrace ); + process.error ); } // Support: Promises/A+ section 2.3.3.3.4.1 @@ -222,10 +222,17 @@ jQuery.extend( { process(); } else { - // Call an optional hook to record the stack, in case of exception + // Call an optional hook to record the error, in case of exception // since it's otherwise lost when execution goes async - if ( jQuery.Deferred.getStackHook ) { - process.stackTrace = jQuery.Deferred.getStackHook(); + if ( jQuery.Deferred.getErrorHook ) { + process.error = jQuery.Deferred.getErrorHook(); + + // The deprecated alias of the above. While the name suggests + // returning the stack, not an error instance, jQuery just passes + // it directly to `console.warn` so both will work; an instance + // just better cooperates with source maps. + } else if ( jQuery.Deferred.getStackHook ) { + process.error = jQuery.Deferred.getStackHook(); } window.setTimeout( process ); } diff --git a/src/deferred/exceptionHook.js b/src/deferred/exceptionHook.js index 6dbdc8520..cc2cf9c26 100644 --- a/src/deferred/exceptionHook.js +++ b/src/deferred/exceptionHook.js @@ -9,12 +9,16 @@ define( [ // 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 ) { +// If `jQuery.Deferred.getErrorHook` is defined, `asyncError` is an error +// captured before the async barrier to get the original error cause +// which may otherwise be hidden. +jQuery.Deferred.exceptionHook = function( error, asyncError ) { // Support: IE 8 - 9 only // 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, error.stack, stack ); + window.console.warn( "jQuery.Deferred exception: " + error.message, + error.stack, asyncError ); } }; diff --git a/test/unit/deferred.js b/test/unit/deferred.js index 184dded95..9a8d4152f 100644 --- a/test/unit/deferred.js +++ b/test/unit/deferred.js @@ -628,13 +628,13 @@ QUnit[ window.console ? "test" : "skip" ]( "jQuery.Deferred.exceptionHook with s defer = jQuery.Deferred(), oldWarn = window.console.warn; - jQuery.Deferred.getStackHook = function() { + jQuery.Deferred.getErrorHook = 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 + // but a custom getErrorHook+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"; + return "NO ERROR FOR YOU"; }; window.console.warn = function() { @@ -656,13 +656,13 @@ QUnit[ window.console ? "test" : "skip" ]( "jQuery.Deferred.exceptionHook with s } else { assert.ok( /cough_up_hairball/.test( msg ), "Function mentioned: " + msg ); } - assert.ok( /NO STACK FOR YOU/.test( msg ), "Stack trace included: " + msg ); + assert.ok( /NO ERROR FOR YOU/.test( msg ), "Error included: " + msg ); }; defer.then( function() { jQuery.cough_up_hairball(); } ).then( null, function( ) { window.console.warn = oldWarn; - delete jQuery.Deferred.getStackHook; + delete jQuery.Deferred.getErrorHook; done(); } ); diff --git a/test/unit/deprecated.js b/test/unit/deprecated.js index 20bea3d2c..ce03f25d8 100644 --- a/test/unit/deprecated.js +++ b/test/unit/deprecated.js @@ -661,4 +661,40 @@ QUnit.test( "trim", function( assert ) { assert.equal( jQuery.trim( "\uFEFF \xA0! | \uFEFF" ), "! |", "leading/trailing should be trimmed" ); } ); +if ( includesModule( "deferred" ) ) { + QUnit.test( "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() { + var msg = Array.prototype.join.call( arguments, " " ); + assert.ok( /cough_up_hairball/.test( msg ), "Function mentioned: " + msg ); + assert.ok( /NO STACK FOR YOU/.test( msg ), "Stack trace included: " + msg ); + }; + + defer.then( function() { + jQuery.cough_up_hairball(); + } ).then( null, function() { + window.console.warn = oldWarn; + delete jQuery.Deferred.getStackHook; + done(); + } ); + + defer.resolve(); + } ); +} + } -- cgit v1.2.3