最近,我们发现 Sentry 中标题为
Event CustomEvent (type=unhandledrejection) captured as promise rejection
的错误急剧增加。根据迄今为止的研究,看起来 Promise 在代码中的某个地方被拒绝了,但我们还没有找到发生这种情况的位置。
我已经添加了下面的代码来记录跟踪条目,其中包含有关被拒绝事件的一些信息,但显然
reason
是 undefined
。我知道侦听器参数上还有一个 promise
属性,但我不知道如何从该对象获取任何有用的信息。是否可以确定承诺的来源?如果可以,如何确定?
window.addEventListener('unhandledrejection', ({ reason }) => {
ErrorManagement.addTrace({
data: { reason },
level: 'info',
message: 'unhandledrejection caught',
category: 'error',
});
});
更新
尝试使用
window.addEventListener('unhandledrejection', console.error)
,它确实按预期工作,但不幸的是,没有包含比我自己的处理程序中可用的更多信息。
受评论的启发,还尝试对 CustomEvent 构造函数进行猴子修补,如下所示。当通过调用
window.dispatch(new CustomEvent('unhandledrejection'))
在控制台中进行测试时,堆栈出现,但在生产中它不会显示在传入事件上。我认为这是由以另一种方式创建的 CustomEvent 引起的,尽管我对 Javascript 不够熟悉,无法肯定地说。
class CustomEventWithStack extends CustomEvent<any> {
stack?;
constructor(type: string, eventInitDict?: CustomEventInit) {
super(type, eventInitDict);
if (type === 'unhandledrejection') {
this.stack = (new Error()).stack;
}
}
}
CustomEventWithStack.prototype = CustomEvent.prototype;
CustomEventWithStack.prototype.constructor = CustomEventWithStack;
CustomEvent = CustomEventWithStack;
您是否也尝试过按照 Pavlo Sobochuk 的建议对 Promise 进行猴子修补?下面的猴子补丁记录了当有人无缘无故拒绝承诺时的堆栈。
const ecmaPromise = Promise;
Promise = class extends ecmaPromise {
constructor(executor) {
super(function(resolve, reject) {
executor(resolve, function(reason) {
if (reason === undefined)
reason = {
stack: new Error().stack
};
reject(reason);
});
});
}
}
window.addEventListener('unhandledrejection', console.error);
new Promise(function(resolve, reject) {
setTimeout(reject, 100);
});