mirror of
https://github.com/jquery/jquery.git
synced 2024-11-23 02:54:22 +00:00
Deferred: Warn on exceptions that are likely programming errors
Fixes gh-2736 Closes gh-2737
This commit is contained in:
parent
bdf1b8f317
commit
36a7cf9b1e
@ -157,12 +157,17 @@ jQuery.extend( {
|
|||||||
mightThrow();
|
mightThrow();
|
||||||
} catch ( e ) {
|
} catch ( e ) {
|
||||||
|
|
||||||
|
if ( jQuery.Deferred.exceptionHook ) {
|
||||||
|
jQuery.Deferred.exceptionHook( e,
|
||||||
|
process.stackTrace );
|
||||||
|
}
|
||||||
|
|
||||||
// Support: Promises/A+ section 2.3.3.3.4.1
|
// Support: Promises/A+ section 2.3.3.3.4.1
|
||||||
// https://promisesaplus.com/#point-61
|
// https://promisesaplus.com/#point-61
|
||||||
// Ignore post-resolution exceptions
|
// Ignore post-resolution exceptions
|
||||||
if ( depth + 1 >= maxDepth ) {
|
if ( depth + 1 >= maxDepth ) {
|
||||||
|
|
||||||
// Only substitue handlers pass on context
|
// Only substitute handlers pass on context
|
||||||
// and multiple values (non-spec behavior)
|
// and multiple values (non-spec behavior)
|
||||||
if ( handler !== Thrower ) {
|
if ( handler !== Thrower ) {
|
||||||
that = undefined;
|
that = undefined;
|
||||||
@ -182,6 +187,12 @@ jQuery.extend( {
|
|||||||
if ( depth ) {
|
if ( depth ) {
|
||||||
process();
|
process();
|
||||||
} else {
|
} 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 );
|
window.setTimeout( process );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
19
src/deferred/exceptionHook.js
Normal file
19
src/deferred/exceptionHook.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
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 );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} );
|
1
src/jquery.js
vendored
1
src/jquery.js
vendored
@ -4,6 +4,7 @@ define( [
|
|||||||
"./traversing",
|
"./traversing",
|
||||||
"./callbacks",
|
"./callbacks",
|
||||||
"./deferred",
|
"./deferred",
|
||||||
|
"./deferred/exceptionHook",
|
||||||
"./core/ready",
|
"./core/ready",
|
||||||
"./data",
|
"./data",
|
||||||
"./queue",
|
"./queue",
|
||||||
|
@ -525,6 +525,65 @@ QUnit.test( "jQuery.Deferred.then - spec compatibility", function( assert ) {
|
|||||||
} catch ( _ ) {}
|
} 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 ) {
|
QUnit.test( "jQuery.Deferred - 1.x/2.x compatibility", function( assert ) {
|
||||||
|
|
||||||
assert.expect( 8 );
|
assert.expect( 8 );
|
||||||
|
Loading…
Reference in New Issue
Block a user