不能从异步承诺执行器函数中抛出错误

Posted

技术标签:

【中文标题】不能从异步承诺执行器函数中抛出错误【英文标题】:Can't throw error from within an async promise executor function 【发布时间】:2017-08-22 08:40:57 【问题描述】:

我一直试图从概念上理解为什么以下代码无法捕获throw。如果您从 new Promise(async (resolve, ... 部分中删除 async 关键字,那么它可以正常工作,因此它与 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>>");
    // ...
  );


答案here、here 和here 重复“如果您在任何其他异步回调中,则必须使用reject”,但“异步”不是指@ 987654331@ 函数,所以我认为他们的解释不适用于这里(如果他们这样做,我不明白如何)。

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

【问题讨论】:

永远不要使用异步执行器函数。你为什么要这样做? @Bergi 在这里肯定掉进了反模式陷阱。我看不出我会如何以不同的方式做这种事情(很可能是因为承诺新手)? --> jsbin.com/waqatagaqa/edit?js(注意thing 是一个我无法控制/没有制作的库) 你想让fn函数async,这样你就可以await新建的promise,然后做进一步的事情。执行者不应该是async,而应该只做一件事:等待事件发生并调用resolve/reject。您可能想用完整的代码提出一个新问题,以便我可以提供正确的答案。 我想我误解了你,所以我按照你的建议创建了一个单独的问题:***.com/questions/43084557/… 谢谢你的帮助! 【参考方案1】:

这是Promise constructor antipattern的异步/等待版本!

Never ever 使用async function 作为Promise 执行器函数(即使你可以让它工作1

[1: 通过调用resolvereject 而不是使用returnthrow 语句]

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

他们也可以。一个不能工作的简单例子是

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

相当于

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

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

Promise constructor 只能捕获 同步 异常,而 async function 从不抛出 - 它总是返回一个承诺(虽然可能会被拒绝)。并且该返回值被忽略,因为 promise 正在等待调用 resolve

【讨论】:

未来的搜索者应该查看this follow up question 以了解有关 Promise 反模式的更多信息。 你实际上可以做得更短:return delay(…).then(function() throw new Error(…); ); @IvanPerevezentsev 在第二个无效示例中,这不是我的答案中的代码吗?【参考方案2】:

因为从 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
    
  

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

【讨论】:

我也一直在做这件事。我最近尝试将我的await 语句移动到function myfunc()...return new Promise... 之间,然后完全删除try/catch,这样我就可以改变我的承诺执行者不再使用async,但这太笨重了.不过,我现在真的很好奇你的这个例子(以及我在项目中所做的一切)是否真的会被视为这种反模式的一部分,如果是这样,为什么,以及什么(真正)更好会有替代品。

以上是关于不能从异步承诺执行器函数中抛出错误的主要内容,如果未能解决你的问题,请参考以下文章

Jasmine:期望在异步函数中抛出错误

Node.JS - 无法使用try / catch块获得异步抛出

setSinkId() 函数在 Electron App 中抛出错误

调用可以抛出,但错误不能从全局变量初始化器中抛出

期望函数在 Jest 中抛出异常

什么是everyauth 承诺?