无法从异步Promise执行器函数中抛出错误

IT技术 javascript node.js promise async-await
2021-02-04 03:14:45

我一直试图从概念上理解为什么以下代码没有捕获throw. 如果您asyncnew Promise(async (resolve, ...部件中删除关键字,则它可以正常工作,因此它与 Promise 执行器是一个异步函数这一事实有关。

(async function() {

  try {
    await fn();
  } catch(e) {
    console.log("CAUGHT fn error -->",e)
  }

})();

function fn() {

  return new Promise(async (resolve, reject) => {
    // ...
    throw new Error("<<fn error>>");
    // ...
  });

}

此处此处此处的答案重复“如果您处于任何其他异步回调中,则必须使用reject”,但“异步”不是指async函数,因此我认为它们的解释不适用于此处(如果他们这样做,我不明白如何)。

如果不是throw我们使用reject,上面的代码工作正常。我想从根本上了解为什么throw在这里不起作用。谢谢!

2个回答

这是Promise构造函数反模式的异步/等待版本

从来没有 使用async function作为一个Promise执行程序功能(即使你可以把它工作1

[1:通过调用resolveandreject而不是使用returnandthrow语句]

通过“异步”,他们不是指async函数,所以我认为他们的解释不适用于这里

他们也可以。无法工作的一个简单示例

new Promise(async function() {
    await delay(…);
    throw new Error(…);
})

这相当于

new Promise(function() {
    return delay(…).then(function() {
        throw new Error(…);
    });
})

现在很明显throw是在异步回调中。

Promise构造也只能望尘莫及同步异常,和一个async function 不会抛出-它总是会返回一个Promise(这可能会遭到拒绝,虽然)。并且该返回值被忽略,因为 Promise 正在等待resolve被调用。

你实际上可以做得更短: return delay(…).then(function() { throw new Error(…); });
2021-03-18 03:14:45
@IvanPerevezentsev 在第二个非工作示例中,这不是我回答中的代码吗?
2021-03-31 03:14:45
未来的搜索者应该看到这个后续问题,了解更多关于 Promise 反模式的信息。
2021-04-03 03:14:45

因为从 Promise 执行器内部与外部世界“通信”的唯一方法是使用resolvereject函数。您可以使用以下示例:

function fn() {
  return new Promise(async (resolve, reject) => {
    // there is no real reason to use an async executor here since there is nothing async happening
    try {
      throw new Error('<<fn error>>')
    } catch(error) {
      return reject(error);
    }
  });
}

例如,当您想做一些具有方便的异步功能但也需要回调的事情时。以下人为示例通过使用异步fs.promises.readFile函数和基于回调的fs.writeFile函数读取文件来复制文件在现实世界中,您永远不会混合这样的fs函数,因为没有必要。但是一些库,如 stylus 和 pug 使用回调,我在这些场景中一直使用这样的东西。

const fs = require('fs');

function copyFile(infilePath, outfilePath) {
  return new Promise(async (resolve, reject) => {
    try {
      // the fs.promises library provides convenient async functions
      const data = await fs.promises.readFile(infilePath);
      // the fs library also provides methods that use callbacks
      // the following line doesn't need a return statement, because there is nothing to return the value to
      // but IMO it is useful to signal intent that the function has completed (especially in more complex functions)
      return fs.writeFile(outfilePath, data, (error) => {
        // note that if there is an error we call the reject function
        // so whether an error is thrown in the promise executor, or the callback the reject function will be called
        // so from the outside, copyFile appears to be a perfectly normal async function
        return (error) ? reject(error) : resolve();
      });
    } catch(error) {
      // this will only catch errors from the main body of the promise executor (ie. the fs.promises.readFile statement
      // it will not catch any errors from the callback to the fs.writeFile statement
      return reject(error);
      // the return statement is not necessary, but IMO communicates the intent that the function is completed
    }
  }
}

显然每个人都说这是一种反模式,但是当我想在做一些只能用回调完成的事情之前做一些异步的事情时,我一直使用它(而不是像我人为的例子那样复制文件)。我不明白为什么人们认为这是一种反模式(使用异步Promise执行器),并且还没有看到一个例子让我相信它应该被接受为一般规则。