【问题标题】:Debugging never resolving promises / async await调试从不解决承诺/异步等待
【发布时间】:2021-12-26 13:36:04
【问题描述】:

在我正在进行的项目中,我们有时会因为错误的承诺永远不会解决而等待永远不会解决。

这真的很难找到哪个 await 导致代码停止运行并找到 node.js 卡在哪里。

这是问题的代码说明。假设“getRandomlyStuckedPromise”是来自外部库的承诺:

getRandomlyStuckedPromise = () => new Promise((resolve) => {
  if (Math.random() > 0.9) return;
  resolve(); // never get called some times
});

await getBadPromise();
await getBadPromise();
await getBadPromise();
await getBadPromise();
await getBadPromise();
await getBadPromise(); // Node.js stuck at one line ... how to know which one ?
await getBadPromise();
await getBadPromise();

如果你已经遇到过这类问题,你知道 node.js 中的一些工具或任何技术可以找到等待 node.js 卡住的地方吗?

【问题讨论】:

  • return 移到resolve() 下。 return 存在当前函数,因此,是的,永远不会调用 resolve。 TBH 你根本不需要退货
  • 这只是为了说明pb ...请阅读以上内容;)
  • 那么实际的问题是什么?为什么你的承诺没有兑现?根据您提供的信息,我们无法确定这一点。它实际上可能是 1,000 种东西
  • 好吧,我最后的问题可能没有很好地表述......我会重写它。我不想知道承诺中的问题(因为它可能来自库或其他任何东西)。我想知道哪个“等待”node.js 卡住了。我将编辑我的问题。
  • @Poyoman 后来我突然想到:如果这都是Puppeteer,你不能用setDefaultNavigationTimeout and setDefaultTimeout给你更好的错误信息吗?或者为任何异常传递一个timeout 参数?

标签: node.js debugging async-await promise


【解决方案1】:

您需要以一种或另一种方式包装或记录承诺。幸运的是,您可以编写一个非常紧凑的包装器。如果您同时等待多个 Promise,您可以将它们排列为一个数组,然后使用 map 包装每个 Promise 以传递到 Promise.all 或任何您需要的地方。

(async () => {

const getBadPromise = () => new Promise((resolve, reject) => {
  if (Math.random() > 0.8) return;
  if (Math.random() > 0.8) reject(new Error("Promise failed on its own"));
  resolve();
});

/**
 * Logs the given promise with the given label.
 *
 * Will reject the promise after a timeout if it does not return.
 */
function wrap(label, promise) {
  const timeout = 1000;
  // Log promise creation.
  console.log(`Start (${label})`);
  return new Promise((resolve, reject) => {
    // Set up timeout handler to reject.
    const timeoutHandler = setTimeout(() => {
      reject(new Error(`Timeout (${label}, ${timeout})`));
    }, timeout);
    // Promisify value (in case it's a primitive). Once it resolves,
    // log it, clear the timeout, and pass the results out of the function.
    Promise.resolve(promise).finally(() => {
      console.log(`End (${label})`);
      clearTimeout(timeoutHandler);
    }).then(resolve, reject);
  });
}

console.log("Start.");
await wrap("1", getBadPromise());
await wrap("2", getBadPromise());
await wrap("3", getBadPromise());
await wrap("4", getBadPromise());
await wrap("5", getBadPromise());
await wrap("6", getBadPromise());
await wrap("7", getBadPromise());
await wrap("8", getBadPromise());
await wrap("9", getBadPromise());
console.log("Done.");

})().catch(console.error);

【讨论】:

  • 好建议杰夫! :) 它可以解决问题,而且不会太重。如果有涉及调试工具的解决方案,我会稍等。但是这个解决了我的问题!
【解决方案2】:

基于@JeffBowman 的提议,我做了一个稍微不同的包装器 =>

const withRejectTimeout = (promise, timeout = 120000 /* 2mn */) => {
  const timeoutError = new Error('Promise timeout');

  return Promise.race([
    promise,
    new Promise((_, reject) => setTimeout(() => reject(timeoutError), timeout)),
  ]);
};

await withRejectTimeout(getBadPromise());
await withRejectTimeout(getBadPromise());
await withRejectTimeout(getBadPromise());
await withRejectTimeout(getBadPromise());
await withRejectTimeout(getBadPromise());
await withRejectTimeout(getBadPromise());
await withRejectTimeout(getBadPromise());

这使用错误堆栈跟踪来帮助定位导致等待的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-09
    • 2018-04-17
    • 2019-09-29
    • 1970-01-01
    • 2019-04-29
    • 2017-06-15
    • 1970-01-01
    • 2018-02-03
    相关资源
    最近更新 更多