try/catch 在堆栈中导致 <anonymous>
try/catch causes <anonymous> in stack
如果我在我的一个调用中有一个 try/catch
,它会导致堆栈在该点变为 "reset"。因此我有 2 个问题 -
1) 为什么会这样?我想这与 v8 引擎的工作方式有关,但知道原因会很有趣。
2) 是否有使用 async/await
并仍然保留整个堆栈跟踪的好的解决方案?现在我将 try/catch
一直放在函数调用链中,并将一个错误滚动到一个新的错误中,一直返回(使用 VError)。
以下代码给出了我期望的堆栈跟踪
async function one() {
throw new Error("blah");
}
async function two() {
await one();
}
async function three() {
await two();
}
async function four() {
try {
await three();
} catch (e) {
console.log(e);
}
}
four();
堆栈跟踪
Error: blah
at one (/dev/async-stack/correct-stack.js:2:9)
at two (/dev/async-stack/correct-stack.js:6:9)
at three (/dev/async-stack/correct-stack.js:10:9)
at four (/dev/async-stack/correct-stack.js:15:11)
at Object.<anonymous> (/dev/async-stack/correct-stack.js:21:1)
at Module._compile (module.js:652:30)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
在中间放入 try/catch
会导致堆栈跟踪从最后一个 try/catch
所在的位置开始。
async function one() {
throw new Error("blah");
}
async function breaker() {
return true;
}
async function stack() {
try {
await breaker();
} catch (error) {
throw error;
}
}
async function two() {
await stack(); // <-- this call
await one();
}
async function three() {
await two();
}
async function four() {
try {
await three();
} catch (e) {
console.log(e);
}
}
four();
堆栈跟踪
Error: blah
at one (/dev/async-stack/broken-stack.js:2:9)
at two (/dev/async-stack/broken-stack.js:19:9)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
at Function.Module.runMain (module.js:695:11)
at startup (bootstrap_node.js:188:16)
at bootstrap_node.js:609:3
这就是 async
/await
语法脱糖的方式。对于你的第一个片段,它就像1
function one() {
return Promise.reject(new Error("blah"));
}
function two() {
return one().then(() => {});
}
function three() {
return two().then(() => {});
}
function four() {
return three().then(() => {}).catch(e => { console.log(e); });
}
four();
而您的第二个代码段的工作方式类似于1
function one() {
return Promise.reject(new Error("blah"));
}
function breaker() {
return Promise.resolve(true);
}
function stack() {
return breaker().then(() => {}).catch(error => { throw error; });
}
function two() {
return stack().then(() => {
// ^^^^^^^ this anonymous function
return one().then(() => {});
})
}
function three() {
return two().then(() => {});
}
function four() {
return three().then(() => {}).catch(e => { console.log(e); });
}
four();
如您所见,one()
确实是从匿名 then
回调中调用的。正如您的标题所示,它实际上与 try
/catch
没有任何关系,而是任何 await
在 one()
调用之前。
1:忽略诸如 Promise
构造函数调用之类的细节,这可能在内部使用了延迟模式,因此它们不会出现在堆栈跟踪中。
一种更迂腐的方法是写 function() { var _resolve, _reject,
_promise = new Promise((res, rej) => { _resolve = res; _reject = rej; }); try { /* function body */ _resolve(_return_value); } catch(e) { _reject(e); } return _promise; }
如果我在我的一个调用中有一个 try/catch
,它会导致堆栈在该点变为 "reset"。因此我有 2 个问题 -
1) 为什么会这样?我想这与 v8 引擎的工作方式有关,但知道原因会很有趣。
2) 是否有使用 async/await
并仍然保留整个堆栈跟踪的好的解决方案?现在我将 try/catch
一直放在函数调用链中,并将一个错误滚动到一个新的错误中,一直返回(使用 VError)。
以下代码给出了我期望的堆栈跟踪
async function one() {
throw new Error("blah");
}
async function two() {
await one();
}
async function three() {
await two();
}
async function four() {
try {
await three();
} catch (e) {
console.log(e);
}
}
four();
堆栈跟踪
Error: blah
at one (/dev/async-stack/correct-stack.js:2:9)
at two (/dev/async-stack/correct-stack.js:6:9)
at three (/dev/async-stack/correct-stack.js:10:9)
at four (/dev/async-stack/correct-stack.js:15:11)
at Object.<anonymous> (/dev/async-stack/correct-stack.js:21:1)
at Module._compile (module.js:652:30)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
在中间放入 try/catch
会导致堆栈跟踪从最后一个 try/catch
所在的位置开始。
async function one() {
throw new Error("blah");
}
async function breaker() {
return true;
}
async function stack() {
try {
await breaker();
} catch (error) {
throw error;
}
}
async function two() {
await stack(); // <-- this call
await one();
}
async function three() {
await two();
}
async function four() {
try {
await three();
} catch (e) {
console.log(e);
}
}
four();
堆栈跟踪
Error: blah
at one (/dev/async-stack/broken-stack.js:2:9)
at two (/dev/async-stack/broken-stack.js:19:9)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
at Function.Module.runMain (module.js:695:11)
at startup (bootstrap_node.js:188:16)
at bootstrap_node.js:609:3
这就是 async
/await
语法脱糖的方式。对于你的第一个片段,它就像1
function one() {
return Promise.reject(new Error("blah"));
}
function two() {
return one().then(() => {});
}
function three() {
return two().then(() => {});
}
function four() {
return three().then(() => {}).catch(e => { console.log(e); });
}
four();
而您的第二个代码段的工作方式类似于1
function one() {
return Promise.reject(new Error("blah"));
}
function breaker() {
return Promise.resolve(true);
}
function stack() {
return breaker().then(() => {}).catch(error => { throw error; });
}
function two() {
return stack().then(() => {
// ^^^^^^^ this anonymous function
return one().then(() => {});
})
}
function three() {
return two().then(() => {});
}
function four() {
return three().then(() => {}).catch(e => { console.log(e); });
}
four();
如您所见,one()
确实是从匿名 then
回调中调用的。正如您的标题所示,它实际上与 try
/catch
没有任何关系,而是任何 await
在 one()
调用之前。
1:忽略诸如 Promise
构造函数调用之类的细节,这可能在内部使用了延迟模式,因此它们不会出现在堆栈跟踪中。
一种更迂腐的方法是写 function() { var _resolve, _reject,
_promise = new Promise((res, rej) => { _resolve = res; _reject = rej; }); try { /* function body */ _resolve(_return_value); } catch(e) { _reject(e); } return _promise; }