在下面的代码中,(玩具)函数
f1
和f2
来自库,因此我无法更改它们。
function f1() { // cannot change this
var x;
alert(x.y); // throws an error because x is undefined
}
function f2() { // cannot change this either
setTimeout(f1, 1000); // creates an asynchronous timeout
}
现在我想捕获错误:
try { // does not work because of the asynchronous environment
f2();
} catch (error) {
console.log('caught: ' + error);
}
但我知道这是行不通的,因为
setTimeout
创建的环境不是为其指定 try-catch 块的环境。
是否有其他可能捕获错误?
您可以通过覆盖
f1
功能来做到这一点...
// original functions -- this is all the code you can't modify
function f1() {
throw "Oh no - an exception!";
}
function f2() {
try {
setTimeout(f1, 1000);
}
catch(e) {
alert(e + " - this is handled in the original code");
}
}
// end of the code you can't modify
// keep a reference to the original f1 function
var __f1 = f1;
// override the original f1 function
var f1 = function() {
try {
// this calls the original version of f1(), along with any parameters
return __f1.apply(this, arguments);
}
catch(e) {
alert(e + " - this is handled in your new code");
}
};
// execute...
f2();
如果感兴趣的封闭第三方功能......在这里
f1
......是可访问的,一种方法/功能修饰符方法,它提供了一个实现功能的抽象层,例如afterThrowing
或 afterFinally
,非常方便,将在下一个提供的示例代码中证明...
// closed third party code that can not be changed.
function f1() {
var x;
// raises an exception because of an undeclared `x` reference.
alert(x.y);
console.log('... should never bee logged.')
}
// closed third party code that can not be changed either.
function f2() {
// invocation happens deferred/asynchronous.
setTimeout(f1, 2000);
console.log('... main thread finished.');
}
function handleThirdPartyInvocationException(exception) {
const { message, stack } = exception;
console.log(
'... handling of following delayed third party function invocation error ...', {
message, stack
}
);
}
// - reassign the accessable third party namespace/reference
// with a modified, exception handling, version of itself.
f1 = f1.afterThrowing(handleThirdPartyInvocationException);
// - invoke third party main thread.
f2();
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
function isFunction(value) {
return (
typeof value === 'function' &&
typeof value.call === 'function' &&
typeof value.apply === 'function'
);
}
function getSanitizedTarget(value) {
return value ?? null;
}
function afterThrowing(handler, target) {
target = getSanitizedTarget(target);
const proceed = this;
return (
isFunction(handler) &&
isFunction(proceed) &&
function afterThrowingType(...argumentArray) {
const context = getSanitizedTarget(this) ?? target;
let result;
try {
result = proceed.apply(context, argumentArray);
} catch (exception) {
result = handler.call(context, exception, argumentArray);
}
return result;
}
) || proceed;
}
Reflect.defineProperty(Function.prototype, 'afterThrowing', {
configurable: true,
writable: true,
value: afterThrowing,
});
</script>