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 没有任何关系,而是任何 awaitone() 调用之前。

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; }