Javascript 将异步函数作为回调传递会导致错误堆栈跟踪丢失
我正在尝试编写一个函数,当抛出对象文本时,该函数将重新引入堆栈跟踪。(). 我注意到,如果一个调用函数将一个异步函数作为回调传递给另一个异步调用函数,如果调用函数有一个try/catch,并捕获任何错误并抛出一个新错误,那么堆栈跟踪就会丢失 我尝试了几种不同的方法:Javascript 将异步函数作为回调传递会导致错误堆栈跟踪丢失,javascript,error-handling,async-await,try-catch,stack-trace,Javascript,Error Handling,Async Await,Try Catch,Stack Trace,我正在尝试编写一个函数,当抛出对象文本时,该函数将重新引入堆栈跟踪。(). 我注意到,如果一个调用函数将一个异步函数作为回调传递给另一个异步调用函数,如果调用函数有一个try/catch,并捕获任何错误并抛出一个新错误,那么堆栈跟踪就会丢失 我尝试了几种不同的方法: function alpha() { throw Error("I am an error!"); } function alphaObectLiberal() { throw "I am an object liter
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();
()
输出:在堆栈跟踪中记录我的注释
[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
[nodemon]正在启动`node src/index.js localhost 8080`
错误:我是一个错误!
在alpha(/sandbox/src/index.js:2:9)
在syncFunctionCaller(/sandbox/src/index.js:6:10)
在任何地方(/sandbox/src/index.js:43:5)
//我们可以看到是什么函数导致了这个错误
反对。(/sandbox/src/index.js:73:1)
at模块编译(内部/modules/cjs/loader.js:776:30)
在Object.Module._extensions..js(internal/modules/cjs/loader.js:787:10)
at Module.load(内部/modules/cjs/loader.js:653:32)
在tryModuleLoad(内部/modules/cjs/loader.js:593:12)
at Function.Module._load(内部/modules/cjs/loader.js:585:3)
位于Function.Module.runMain(internal/modules/cjs/loader.js:829:12)
错误:我是一个对象文字!
在syncFunctionCaller2(/sandbox/src/index.js:17:11)
在任何地方(/sandbox/src/index.js:65:5)
//在同步包装器中,堆栈跟踪被保留
反对。(/sandbox/src/index.js:95:1)
at模块编译(内部/modules/cjs/loader.js:776:30)
在Object.Module._extensions..js(internal/modules/cjs/loader.js:787:10)
at Module.load(内部/modules/cjs/loader.js:653:32)
在tryModuleLoad(内部/modules/cjs/loader.js:593:12)
at Function.Module._load(内部/modules/cjs/loader.js:585:3)
位于Function.Module.runMain(internal/modules/cjs/loader.js:829:12)
启动时(内部/bootstrap/node.js:283:19)
错误:我也是一个错误!
在asyncAlpha(/sandbox/src/index.js:10:9)
异步函数调用方(/sandbox/src/index.js:18:16)
在任何地方(/sandbox/src/index.js:49:11)
//我们可以看到是什么函数导致了这个错误
反对。(/sandbox/src/index.js:73:1)
at模块编译(内部/modules/cjs/loader.js:776:30)
在Object.Module._extensions..js(internal/modules/cjs/loader.js:787:10)
at Module.load(内部/modules/cjs/loader.js:653:32)
在tryModuleLoad(内部/modules/cjs/loader.js:593:12)
at Function.Module._load(内部/modules/cjs/loader.js:585:3)
位于Function.Module.runMain(internal/modules/cjs/loader.js:829:12)
错误:我是一个对象文字!
在asyncFunctionCaller2(/sandbox/src/index.js:25:11)
//我们失去了一切的线索`
在进程中。_tick回调(内部/process/next_tick.js:68:7)
位于Function.Module.runMain(internal/modules/cjs/loader.js:832:11)
启动时(内部/bootstrap/node.js:283:19)
在bootstrapNodeJSCore(internal/bootstrap/node.js:622:3)
错误:我是函数调用方抛出的错误!
在asyncFunctionCaller3(/sandbox/src/index.js:33:11)
//我们失去了一切的线索`
在进程中。_tick回调(内部/process/next_tick.js:68:7)
位于Function.Module.runMain(internal/modules/cjs/loader.js:832:11)
启动时(内部/bootstrap/node.js:283:19)
在bootstrapNodeJSCore(internal/bootstrap/node.js:622:3)
错误:此处没有尝试捕捉!
在asyncFunctionCaller4(/sandbox/src/index.js:38:9)
在任何地方(/sandbox/src/index.js:67:11)
//我们可以看到是什么函数导致了这个错误
在进程中。_tick回调(内部/process/next_tick.js:68:7)
位于Function.Module.runMain(internal/modules/cjs/loader.js:832:11)
启动时(内部/bootstrap/node.js:283:19)
在bootstrapNodeJSCore(internal/bootstrap/node.js:622:3)
[nodemon]清除退出-在重新启动前等待更改
在我看来,等待声明是把事情搞砸的原因
这是怎么回事 编辑:这个答案似乎绝对不正确,请参阅@andy的答案,它准确地描述了这里发生的事情 我认为上下文并没有完全丢失——它从未出现过。您使用的是async/await,您的代码被有效地分割为“块”,这些块以某种非线性方式异步执行。这意味着解释器在某些时候离开主线程,做一个“勾选”(这样您就可以在stacktrace中看到
process.\u tickCallback
),然后执行下一个“块”
为什么会这样?因为async/await是Promise
的一种语法糖,它有效地由外部事件引导的包装良好的回调(我相信在这种特殊情况下它是一个计时器)
对此你能做些什么?也许,我不能肯定,因为我从来没有这样做过。但我认为以下是一个良好的开端:缺少堆栈跟踪与承诺无关。编写相同的代码,使函数以同步方式相互调用,您将观察到完全相同的行为,即在重新刷新
新错误时丢失完整的堆栈跟踪数据。只有Error
对象提供堆栈访问。它又由负责捕获交叉堆栈帧的堆栈跟踪的本机代码(like)支持。每次创建Error
对象时,它都会从堆栈帧的这一点捕获堆栈(至少在浏览器中可以观察到,nodejs实现可能会有所不同)。所以,如果您捕获并追溯不同的错误
对象,则其堆栈跟踪在冒泡异常的顶部可见。由于错误
缺少异常链接(无法将新异常包装到捕获的异常周围),因此很难填补这些空白。更有趣的是,它根本不引入Error.prototype.stack
属性,而在MDN中,您会发现JS引擎是非标准扩展
编辑:关于堆栈上缺少“一切”函数,这是引擎如何传输的副作用
async function asyncFunctionCaller2(fn) {
try {
await fn();
} catch (err) {
throw new Error(err);
}
}
async function asyncFunctionCaller2(fn) {
let [ data, err ] = await awaitCatcher(fn);
// Now you can do whatever you want with data or error
if ( err ) throw err;
if ( data ) return data;
}
// Note:
// You can name the variables whatever you want.
// They don't have to be "data" or "err"
interface promiseType {
test: string
}
(async () => {
let p = Promise.resolve({test: "hi mom"})
let [ data , error ] = await awaitCatcher<promiseType>(p);
console.log(data, error);
})()
const { awaitCatcher } = require("await-catcher");
async function asyncAlphaObjectLiteral() {
throw Error("I am an object literal!"); // 1) You need to create an Error object here
// ~~~~> try throwing just a string and see the difference
}
async function asyncFunctionCaller2(fn) {
try {
await fn();
} catch (err) {
throw err; // 2) Don't create a new error, just throw the error.
}
}
/**
* Or you can just do this...
* the "awaitCatcher" will catch the errors :)
*
* async function asyncFunctionCaller2(fn) {
* await fn();
* }
*/
async function everything() {
/**
* notice we don't need try/catch here either!
*/
let [data, error] = await awaitCatcher(
asyncFunctionCaller2(asyncAlphaObjectLiteral)
);
console.log(error); // 3) Now you have the full error stack trace
}
everything();