【问题标题】:How to handle exception resulting in unresolved promise如何处理导致未解决的承诺的异常
【发布时间】:2019-05-19 10:39:32
【问题描述】:

在处理由于不可预见的未捕获异常而无法解决的承诺时,我看到了一些不一致的行为。似乎取决于我如何链接承诺会改变这个承诺是否会解决,我不明白为什么。

这是不好的功能:

function bad() {
  return new Promise(resolve => {
    setTimeout(() => {
      throw 'unforseen exception!';
      resolve();
    }, 50);
  });
}

如果我以这些方式调用此函数,它不会解析:

bad().then(() => console.log('resolved')); // no console logging

try {
  await bad();
} catch(e) {
  console.log(e);
}
console.log('resolved'); // no console logging

但是这样称呼它确实可以解决:

Promise.resolve().then(bad()).then(() => console.log('resolved')); // console logs "resolved"

为什么会这样? 编辑:我现在明白我做错了什么。但我真正想回答的是下一部分。

当我有一系列需要连续运行并且需要继续执行的 Promise 链时,我该如何最好地保护自己免受意外异常的影响,即使链中某处出现故障也是如此?

我也尝试过使用 catch 或 finally,但它们似乎没有任何区别。一旦达到未解决的承诺,执行就会失败。

【问题讨论】:

  • 一个问题是.then(bad()) ... .then 参数是一个函数,而不是像你正在做的那样,调用函数的结果 - 你应该改为.then(bad) -
  • @Jaromanda X 我并没有真正看到一个更正确的。我只是对为什么一个人的行为不同感到困惑。感谢您指出我的错误。
  • 是的,我删除了评论的那部分 - 这不是唯一的问题,但这就是为什么最后一个代码片段的代码行为不同

标签: javascript exception callback promise es6-promise


【解决方案1】:

问题在于bad() 以异步方式抛出错误,使得调用者无法检测到错误。如果你想在new Promise... 段内抛出错误,你应该调用reject 函数:

function bad() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('bad!');
      resolve();
    }, 50);
  });
}


bad()
  .then(() => console.log('resolved'))
  .catch((err) => console.log(err));

(async () => {
  try {
    await bad();
  } catch(e) {
    console.log(e);
  }
  console.log('await finished');
})();

你的原因

Promise.resolve().then(bad()).then

调用下一个.then是因为then接受一个函数作为参数,但是你的bad()在开始时调用bad,而解释器试图提出 Promise 链。如果您将 bad 作为函数参数而不是调用它,您会看到与原始代码中类似的损坏行为 - Promise 永远无法解决:

function bad() {
  return new Promise(resolve => {
    setTimeout(() => {
      throw 'unforseen exception!';
      resolve();
    }, 50);
  });
}

// Promise never resolves:
Promise.resolve().then(bad).then(() => console.log('resolved'));

相比之下,.then(bad()) 将评估为非函数,因此.then立即解决,因此解释器也将立即继续下一个.then

【讨论】:

  • 第一部分的想法是这是一个“不可预见的”异常。为了清楚起见,我只是在这里明确地抛出它,但实际上这是一个更深层次的异常,没有得到正确处理。就我而言,它破坏了我的应用程序的 init 部分。我真正想知道的是,我如何保护自己免受再次发生这种情况?好像没有办法?假设我需要我的承诺以串行方式运行。
  • 应该大致了解在编写脚本时可能会在哪些行引发异常 - 您可以catch 它们然后调用reject
  • 在这种情况下,它是 html image.onload 函数中的跨源异常。而且它只是在新的 chrome 发布后才成为一个问题。在这种情况下并不明显(因为图像 src 是本地的)。这让我想知道未来的其他潜在问题。
【解决方案2】:

在这段代码中:

new Promise(resolve => {
  setTimeout(() => {
    throw 'unforseen exception!';
    resolve();
  }, 50);
});

抛出发生在非异步回调函数中。处理此类事情的方法是在可能抛出的代码上使用 try/catch 语句:

new Promise((resolve, reject) => {
  setTimeout(() => {
    try {
      throw 'unforseen exception!';

      resolve();
    }
    catch (err) {
      reject(err);
    }
  }, 50);
});

【讨论】:

  • 我同意。这似乎是最好的方法。但是当我不知道我的代码的哪些部分将来可能会抛出错误时,我该如何在实践中应用它呢?我是否应该将每个回调包装在 try catch 中的 promise 中以确保它们解决?在这种情况下,它是 html image.onload 函数中的跨源异常。而且它只是在新的 chrome 发布后才成为一个问题。不明显(因为图像 src 是本地的)。我是否需要检查我调用的每个外部函数的来源,以确保我正在处理所有可能的异常?
  • @DeaconMeek 理想情况下,您应该能够通过检查文档来了解函数调用是否会引发错误。如果由于某种原因不可能,也可以将整个回调包装在 try/catch 中。
  • 我会回答这个作为接受,除了它没有提到我在 promise.then 中调用 bad() 函数的错误。我相信正确的解决方案介于您和@CertainPerformance 之间
猜你喜欢
  • 2020-04-21
  • 2019-06-23
  • 2020-11-17
  • 2021-09-27
  • 2016-12-13
  • 2022-01-22
  • 1970-01-01
  • 2018-12-13
  • 2020-03-22
相关资源
最近更新 更多