当我仅使用onunhandledrejection
处理程序捕获Promise拒绝时,如何确定发生在哪里?
console.error = ()=>{}
window.addEventListener('unhandledrejection', (promiseRejectionEvent) => {
console.log('unhandled: ', Error().stack)
})
function main() {
new Promise(() => { throw null })
}
main()
如果在运行此命令后检查浏览器的控制台,则会看到类似以下内容的
:
Error().stack
仅在其堆栈跟踪中包含拒绝处理程序函数本身(灰色输出js:14:30
)。但是浏览器确实似乎知道拒绝发生在哪里:还有另一个红色错误输出(Uncaught (in promise) null
),指向目标行(js:18
)。如何访问此行信息?
似乎后者的输出是由浏览器的内部完成的,因为不能像上面的示例那样通过覆盖console.error
来防止。如MDN所述,只能通过调用promiseRejectionEvent.preventDefault()
来防止这种情况。但是我还是不想阻止它,而是取而代之,例如出于记录目的。
现实世界中的用例:当然可以不依赖onunhandledrejection
事件处理程序,例如通过添加一个.catch()
短语或至少抛出throw new Error(null)
。但就我而言,我无法控制它,因为它是第三方代码。今天,它在客户端的浏览器中意外抛出(可能是库错误),并且自动错误报告未包含堆栈跟踪。我试图缩小上述潜在问题的范围。谢谢!
根据评论进行编辑:
在try / catch中包装第三方代码? – weltschmerz
好点,但这无济于事,因为拒绝实际上发生在回调内部:
window.addEventListener('unhandledrejection', (promiseRejectionEvent) => {
console.log('unhandled: ', Error().stack) // <- stack once again does *not* include "main()", it is only printed out in the console
})
function main() {
try {
thirdPartyModule()
} catch(e) {
// Never caught
console.log("caught:", e)
}
}
// Example code
// We cannot change this function
function thirdPartyModule() {
setTimeout(() =>
new Promise(() =>
{ throw null }))
}
main()
目前还没有很好的解决方案来跟踪异步堆栈跟踪,但是可以使用Zone.js进行跟踪。如果您在Zone.js页面上查看演示,则有一个异步堆栈跟踪的示例。
Zone通过猴子修补所有创建异步任务以实现此目的的本机API来工作。
不可能。
我想象您想要的“堆栈跟踪”将包含带有throw null;
的行,但是,当调用unhandledrejection
事件处理程序时,不在堆栈中。执行throw null;
时,不直接(同步)调用该处理程序,而是将一个调用该处理程序的微任务排队。 (有关事件循环,任务和微任务的说明,请参见"In The Loop" by Jake Archibald。)
可以通过在引发错误之前对微任务排队来测试。如果throwing同步调用处理程序,则微任务应在其后执行,但是如果throwing将调用该处理程序的微任务排队,则第一个微任务先执行,然后执行第二个(调用处理程序)。
window.addEventListener('unhandledrejection', (promiseRejectionEvent) => {
console.log('unhandled: ', Error().stack) // <- stack once again does *not* include "main()", it is only printed out in the console
})
function main() {
try {
thirdPartyModule()
} catch (e) {
// Never caught
console.log("caught:", e)
}
}
// Example code
// We cannot change this function
function thirdPartyModule() {
setTimeout(() =>
new Promise(() => {
Promise.resolve().then(() => { // Queue a microtask before throwing
console.log("Microtask")
})
throw null
}))
}
main()