addEventListener 回调中的错误处理
Error handling inside addEventListener callback
如果开发人员想要拥有顶级错误处理功能,他们如何构建他们的程序?
我的第一反应是将 try..catch 包装到主函数中,但是,这不会触发回调错误?
try {
main();
} catch(error) {
alert(error)
}
function main() {
// This works
throw new Error('Error from main()');
document.querySelector('button').addEventListener('click', function() {
// This doesn throw
throw new Error ('Error from click callback');
})
}
<button>
Click me to see my callback error
</button>
在javascript中你可以覆盖全局onerror,捕获大部分错误:
window.onerror = function(message, source, lineno, colno, error) { ... };
https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror
你的情况:
window.onerror = function(message, source, lineno, colno, error) {
console.error(message);
alert(message);
return false
};
function main() {
// This works
throw new Error('Error from main()');
document.querySelector('button').addEventListener('click', function() {
// This doesn throw
throw new Error ('Error from click callback');
})
}
main();
一些额外信息:
https://blog.sentry.io/2016/01/04/client-javascript-reporting-window-onerror
在 Promise 是否会引发错误的问题之后添加,让我们测试一下:
window.onerror = (message, source, lineno,colno,error)=>{
console.error(`It does!, ${message}`);
};
const aFn = ()=>{
return new Promise((resolve)=>{
setTimeout(()=>{
throw new Error("whoops")
}, 3000);
});
}
aFn();
结果:
VM1163:2 It does!, Script error.
window.onerror @ VM1163:2
error (asynchroon)
(anoniem) @ VM1163:1
VM1163:7 Uncaught Error: whoops
at <anonymous>:7:19
围绕现有 functions/methods 的 Try-catch 功能最好通过包装方法实现。
对于 OP 的用例,需要一个修改包装函数,它明确针对“抛出后”的处理...
// - try-catch wrapper which specifically
// targets the handling of "after throwing".
function afterThrowingModifier(proceed, handler, target) {
return function (...argsArray) {
let result;
try {
result = proceed.apply(target, argsArray);
} catch (exception) {
result = handler.call(target, exception, argsArray);
}
return result;
}
}
function failingClickHandler(/* event */) {
throw new Error('Error from click callback');
}
function afterTrowingHandler(error, [ event ]) {
const { message, stack } = error
const { type, currentTarget } = event;
console.log({
error: { message, stack },
event: { type, currentTarget },
});
}
function main() {
document
.querySelector('button')
.addEventListener('click', afterThrowingModifier(
failingClickHandler, afterTrowingHandler
));
}
main();
body { margin: 0; }
.as-console-wrapper { min-height: 85%!important; }
<button>
Click me to see my callback error
</button>
其中一个原因可以为 like afterThrowing
or afterFinally
实现基于原型的抽象。然后上面的 main
示例代码更改为更具表现力的内容,例如 ...
function afterTrowingHandler(error, [ event ]) {
const { message, stack } = error
const { type, currentTarget } = event;
console.log({
error: { message, stack },
event: { type, currentTarget },
});
}
function main() {
document
.querySelector('button')
.addEventListener('click', (function (/* event */) {
throw new Error('Error from click callback');
}).afterThrowing(afterTrowingHandler));
}
main();
body { margin: 0; }
.as-console-wrapper { min-height: 85%!important; }
<button>
Click me to see my callback error
</button>
<script>
(function (Function) {
function isFunction(value) {
return (
typeof value === 'function' &&
typeof value.call === 'function' &&
typeof value.apply === 'function'
);
}
function getSanitizedTarget(value) {
return value ?? null;
}
function afterThrowing/*Modifier*/(handler, target) {
target = getSanitizedTarget(target);
const proceed = this;
return (
isFunction(handler) &&
isFunction(proceed) &&
function afterThrowingType(...argumentArray) {
const context = getSanitizedTarget(this) ?? target;
let result;
try {
// try the invocation of the original function.
result = proceed.apply(context, argumentArray);
} catch (exception) {
result = handler.call(context, exception, argumentArray);
}
return result;
}
) || proceed;
}
// afterThrowing.toString = () => 'afterThrowing() { [native code] }';
Object.defineProperty(Function.prototype, 'afterThrowing', {
configurable: true,
writable: true,
value: afterThrowing/*Modifier*/
});
}(Function));
</script>
如果开发人员想要拥有顶级错误处理功能,他们如何构建他们的程序?
我的第一反应是将 try..catch 包装到主函数中,但是,这不会触发回调错误?
try {
main();
} catch(error) {
alert(error)
}
function main() {
// This works
throw new Error('Error from main()');
document.querySelector('button').addEventListener('click', function() {
// This doesn throw
throw new Error ('Error from click callback');
})
}
<button>
Click me to see my callback error
</button>
在javascript中你可以覆盖全局onerror,捕获大部分错误:
window.onerror = function(message, source, lineno, colno, error) { ... };
https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror
你的情况:
window.onerror = function(message, source, lineno, colno, error) {
console.error(message);
alert(message);
return false
};
function main() {
// This works
throw new Error('Error from main()');
document.querySelector('button').addEventListener('click', function() {
// This doesn throw
throw new Error ('Error from click callback');
})
}
main();
一些额外信息: https://blog.sentry.io/2016/01/04/client-javascript-reporting-window-onerror
在 Promise 是否会引发错误的问题之后添加,让我们测试一下:
window.onerror = (message, source, lineno,colno,error)=>{
console.error(`It does!, ${message}`);
};
const aFn = ()=>{
return new Promise((resolve)=>{
setTimeout(()=>{
throw new Error("whoops")
}, 3000);
});
}
aFn();
结果:
VM1163:2 It does!, Script error.
window.onerror @ VM1163:2
error (asynchroon)
(anoniem) @ VM1163:1
VM1163:7 Uncaught Error: whoops
at <anonymous>:7:19
围绕现有 functions/methods 的 Try-catch 功能最好通过包装方法实现。
对于 OP 的用例,需要一个修改包装函数,它明确针对“抛出后”的处理...
// - try-catch wrapper which specifically
// targets the handling of "after throwing".
function afterThrowingModifier(proceed, handler, target) {
return function (...argsArray) {
let result;
try {
result = proceed.apply(target, argsArray);
} catch (exception) {
result = handler.call(target, exception, argsArray);
}
return result;
}
}
function failingClickHandler(/* event */) {
throw new Error('Error from click callback');
}
function afterTrowingHandler(error, [ event ]) {
const { message, stack } = error
const { type, currentTarget } = event;
console.log({
error: { message, stack },
event: { type, currentTarget },
});
}
function main() {
document
.querySelector('button')
.addEventListener('click', afterThrowingModifier(
failingClickHandler, afterTrowingHandler
));
}
main();
body { margin: 0; }
.as-console-wrapper { min-height: 85%!important; }
<button>
Click me to see my callback error
</button>
其中一个原因可以为 afterThrowing
or afterFinally
实现基于原型的抽象。然后上面的 main
示例代码更改为更具表现力的内容,例如 ...
function afterTrowingHandler(error, [ event ]) {
const { message, stack } = error
const { type, currentTarget } = event;
console.log({
error: { message, stack },
event: { type, currentTarget },
});
}
function main() {
document
.querySelector('button')
.addEventListener('click', (function (/* event */) {
throw new Error('Error from click callback');
}).afterThrowing(afterTrowingHandler));
}
main();
body { margin: 0; }
.as-console-wrapper { min-height: 85%!important; }
<button>
Click me to see my callback error
</button>
<script>
(function (Function) {
function isFunction(value) {
return (
typeof value === 'function' &&
typeof value.call === 'function' &&
typeof value.apply === 'function'
);
}
function getSanitizedTarget(value) {
return value ?? null;
}
function afterThrowing/*Modifier*/(handler, target) {
target = getSanitizedTarget(target);
const proceed = this;
return (
isFunction(handler) &&
isFunction(proceed) &&
function afterThrowingType(...argumentArray) {
const context = getSanitizedTarget(this) ?? target;
let result;
try {
// try the invocation of the original function.
result = proceed.apply(context, argumentArray);
} catch (exception) {
result = handler.call(context, exception, argumentArray);
}
return result;
}
) || proceed;
}
// afterThrowing.toString = () => 'afterThrowing() { [native code] }';
Object.defineProperty(Function.prototype, 'afterThrowing', {
configurable: true,
writable: true,
value: afterThrowing/*Modifier*/
});
}(Function));
</script>