简体   繁体   English

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

[英]Can't throw error from within an async promise executor function

I've been trying to get a conceptual understanding of why the following code doesn't catch the throw .我一直试图从概念上理解为什么下面的代码没有捕捉到throw If you remove the async keyword from the new Promise(async (resolve, ... part then it works fine, so it has to do with the fact that the Promise executor is an async function.如果您从new Promise(async (resolve, ... part ) 中删除async关键字new 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>>");
    // ...
  });

}

The answers here , here , and here repeat that "if you're in any other asynchronous callback, you must use reject ", but by "asynchronous" they're not referring to async functions, so I don't think their explanations apply here (and if they do, I don't understand how). 此处此处此处的答案重复“如果您处于任何其他异步回调中,则必须使用reject ”,但是“异步”不是指async函数,因此我认为它们的解释不适用在这里(如果他们这样做,我不明白如何)。

If instead of throw we use reject , the above code works fine.如果我们使用reject而不是throw ,则上面的代码可以正常工作。 I'd like to understand, fundamentally , why throw doesn't work here.我想从根本上了解为什么throw在这里不起作用。 Thanks!谢谢!

This is the async/await version of the Promise constructor antipattern !这是Promise构造函数反模式的异步/等待版本!

Never ever use an async function as a Promise executor function (even when you can make it work 1 ) !从来没有使用async function作为Promise执行程序功能(即使你可以把它工作1)!

[1: by calling resolve and reject instead of using return and throw statements] [1:通过调用resolvereject而不是使用returnthrow语句]

by "asynchronous" they're not referring to async functions, so I don't think their explanations apply here通过“异步”,他们不是指async函数,所以我认为他们的解释不适用于这里

They could as well.他们也可以。 A simple example where it cannot work is无法工作的一个简单示例是

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

which is equivalent to这相当于

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

where it's clear now that the throw is inside an asynchronous callback.现在很清楚throw在异步回调中。

The Promise constructor can only catch synchronous exceptions, and an async function never throws - it always returns a promise (which might get rejected though). Promise构造函数只能捕获同步异常,而async function永远不会抛出- 它总是返回一个Promise (尽管可能会被拒绝)。 And that return value is ignored, as the promise is waiting for resolve to be called.并且该返回值被忽略,因为承诺正在等待resolve被调用。

because the only way to "communicate" to the outside world from within a Promise executor is to use the resolve and reject functions.因为从 Promise 执行器内部与外部世界“通信”的唯一方法是使用resolvereject函数。 You could use the following for your example:您可以使用以下示例:

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

An example would be when you want to do something that has convenient async functions, but also requires a callback.例如,当您想做一些具有方便的异步功能但也需要回调的事情时。 The following contrived example copies a file by reading it using the async fs.promises.readFile function with the callback based fs.writeFile function.以下人为示例通过使用异步fs.promises.readFile函数和基于回调的fs.writeFile函数读取文件来复制文件。 In the real world, you would never mix fs functions like this because there is no need to.在现实世界中,您永远不会像这样混合fs函数,因为没有必要。 But some libraries like stylus and pug use callbacks, and I use something like this all the time in those scenarios.但是一些库,如 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
    }
  }
}

Apparently everyone says this is an anti-pattern, but I use it all the time when I want to do some async stuff before doing something that can only be done with a callback (not for copying files like my contrived example).显然每个人都说这是一种反模式,但是当我想在做一些只能用回调完成的事情之前做一些异步的事情时,我一直使用它(而不是像我人为的例子那样复制文件)。 I don't understand why people think it is an anti-pattern (to use an async promise executor), and haven't seen an example yet that has convinced me that it should be accepted as a general rule.我不明白为什么人们认为这是一种反模式(使用异步承诺执行器),并且还没有看到一个例子让我相信它应该被接受为一般规则。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM