dwj*_*ton 14 javascript error-handling try-catch stack-trace async-await
我正在尝试编写一个函数,该函数将在引发对象常量时重新引入堆栈跟踪。(请参阅此相关问题)。
我注意到的是,如果将异步函数作为回调传递给另一个异步调用者函数,则如果调用者函数具有try / catch并捕获任何错误并抛出新的Error,则堆栈跟踪会丢失。
我已经尝试了几种方法:
function alpha() {
throw Error("I am an error!");
}
function alphaObectLiberal() {
throw "I am an object literal!"; //Ordinarily this will cause the stack trace to be lost.
}
function syncFunctionCaller(fn) {
return fn();
}
function syncFunctionCaller2(fn) { //This wrapper wraps it in a proper error and subsequently preserves the stack trace.
try {
return fn();
} catch (err) {
throw new Error(err); //Stack trace is preserved when it is synchronous.
}
}
async function asyncAlpha() {
throw Error("I am also an error!"); //Stack trace is preseved if a proper error is thown from callback
}
async function asyncAlphaObjectLiteral() {
throw "I am an object literal!"; //I want to catch this, and convert it to a proper Error object.
}
async function asyncFunctionCaller(fn) {
return await fn();
}
async function asyncFunctionCaller2(fn) {
try {
await fn();
} catch (err) {
throw new Error(err);
}
}
async function asyncFunctionCaller3(fn) {
try {
await fn();
} catch (err) {
throw new Error("I'm an error thrown from the function caller!");
}
}
async function asyncFunctionCaller4(fn) {
throw new Error("No try catch here!");
}
async function everything() {
try {
syncFunctionCaller(alpha);
} catch (err) {
console.log(err);
}
try {
syncFunctionCaller2(alphaObectLiberal);
} catch (err) {
console.log(err);
}
try {
await asyncFunctionCaller(asyncAlpha);
} catch (err) {
console.log(err);
}
try {
await asyncFunctionCaller2(asyncAlphaObjectLiteral);
} catch (err) {
console.log(err); //We've lost the `everthing` line number from the stack trace
}
try {
await asyncFunctionCaller3(asyncAlphaObjectLiteral);
} catch (err) {
console.log(err); //We've lost the `everthing` line number from the stack trace
}
try {
await asyncFunctionCaller4(asyncAlphaObjectLiteral);
} catch (err) {
console.log(err); //This one is fine
}
}
everything();
Run Code Online (Sandbox Code Playgroud)
(代码沙箱)
输出:在堆栈跟踪中记录我的评论
[nodemon] starting `node src/index.js localhost 8080`
Error: I am an error!
at alpha (/sandbox/src/index.js:2:9)
at syncFunctionCaller (/sandbox/src/index.js:6:10)
at everything (/sandbox/src/index.js:43:5)
//We can see what function caused this error
at Object.<anonymous> (/sandbox/src/index.js:73:1)
at Module._compile (internal/modules/cjs/loader.js:776:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:829:12)
Error: I am an object literal!
at syncFunctionCaller2 (/sandbox/src/index.js:17:11)
at everything (/sandbox/src/index.js:65:5)
//In a synchronous wrapper, the stack trace is preserved
at Object.<anonymous> (/sandbox/src/index.js:95:1)
at Module._compile (internal/modules/cjs/loader.js:776:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:829:12)
at startup (internal/bootstrap/node.js:283:19)
Error: I am also an error!
at asyncAlpha (/sandbox/src/index.js:10:9)
at asyncFunctionCaller (/sandbox/src/index.js:18:16)
at everything (/sandbox/src/index.js:49:11)
//We can see what function caused this error
at Object.<anonymous> (/sandbox/src/index.js:73:1)
at Module._compile (internal/modules/cjs/loader.js:776:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:829:12)
Error: I am an object literal!
at asyncFunctionCaller2 (/sandbox/src/index.js:25:11)
//We've lost the stacktrace in `everything`
at process._tickCallback (internal/process/next_tick.js:68:7)
at Function.Module.runMain (internal/modules/cjs/loader.js:832:11)
at startup (internal/bootstrap/node.js:283:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:622:3)
Error: I'm an error thrown from the function caller!
at asyncFunctionCaller3 (/sandbox/src/index.js:33:11)
//We've lost the stacktrace in `everything`
at process._tickCallback (internal/process/next_tick.js:68:7)
at Function.Module.runMain (internal/modules/cjs/loader.js:832:11)
at startup (internal/bootstrap/node.js:283:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:622:3)
Error: No try catch here!
at asyncFunctionCaller4 (/sandbox/src/index.js:38:9)
at everything (/sandbox/src/index.js:67:11)
//We can see what function caused this error
at process._tickCallback (internal/process/next_tick.js:68:7)
at Function.Module.runMain (internal/modules/cjs/loader.js:832:11)
at startup (internal/bootstrap/node.js:283:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:622:3)
[nodemon] clean exit - waiting for changes before restart
Run Code Online (Sandbox Code Playgroud)
在我看来,await语句正在弄糟这一点。
这里发生了什么?
缺少堆栈跟踪与承诺无关。编写具有互相调用函数的相同代码,您将观察到完全相同的行为,即重新抛出时丢失了完整的堆栈跟踪数据new Error
。只有Error
提供堆栈访问的对象。反过来,它由负责捕获交叉堆栈帧的堆栈跟踪的本机代码(如V8引擎)支持。更糟糕的是,每次创建Error
对象时,它都会从这一点开始跨堆栈帧捕获堆栈(至少在浏览器中是可见的,nodejs的实现可能有所不同)。这样,如果您捕获并回溯了另一个Error
对象,则在冒泡异常之上可以看到其堆栈跟踪。缺少链的例外Error
(无法将新的异常包装在捕获的异常周围)使填补这些空白变得困难。更有趣的是ECMA-262规范第19.5章根本没有介绍Error.prototype.stack
属性,在MDN中,您会发现堆栈属性是JS引擎的非标准扩展。
编辑:关于在堆栈上缺少“所有”功能,这是引擎如何将“异步/等待”转换为微任务调用以及谁真正在调用特定的回调的副作用。请参考V8引擎团队的说明,以及有关零配件的零成本异步堆栈跟踪文档。从版本12.x开始的NodeJS 将包含更多更清晰的堆栈跟踪,--async-stack-traces
V8引擎提供的选项中提供了这些跟踪。
归档时间: |
|
查看次数: |
3068 次 |
最近记录: |