用一级 try ... catch 捕获 JavaScript Promise 中的错误

Posted

技术标签:

【中文标题】用一级 try ... catch 捕获 JavaScript Promise 中的错误【英文标题】:Catching Errors in JavaScript Promises with a First Level try ... catch 【发布时间】:2014-09-18 14:15:39 【问题描述】:

所以,我希望我的第一级捕获是处理错误的那个。有没有办法将我的错误传播到第一次捕获?

参考代码,不工作(还):

Promise = require('./framework/libraries/bluebird.js');

function promise() 
    var promise = new Promise(function(resolve, reject) 
        throw('Oh no!');
    );

    promise.catch(function(error) 
        throw(error);
    );


try    
    promise();

// I WANT THIS CATCH TO CATCH THE ERROR THROWN IN THE PROMISE
catch(error) 
    console.log('Caught!', error);

【问题讨论】:

只是让你知道,你要求混淆有两个不同的东西命名为promise(一个函数和一个变量),更不用说内置的Promise。我相信你可以让它工作,但为什么要让你的代码那样混乱呢? 【参考方案1】:

您不能使用 try-catch 语句来处理异步抛出的异常,因为该函数在抛出任何异常之前已经“返回”。您应该改用promise.thenpromise.catch 方法,它们代表try-catch 语句的异步等效项。 (或者使用@Edo 的回答中提到的 async/await 语法。)

您需要做的是返回承诺,然后将另一个 .catch 链接到它:

function promise() 
    var promise = new Promise(function(resolve, reject) 
        throw('Oh no!');
    );

    return promise.catch(function(error) 
        throw(error);
    );


promise().catch(function(error) 
    console.log('Caught!', error);
);

Promise 是可链接的,因此如果一个 Promise 重新抛出错误,它将被委托给下一个 .catch

顺便说一句,您不需要在throw 语句周围使用括号(throw athrow(a) 相同)。

【讨论】:

@Kirk:如果你不能编辑这个promise 函数,我只会这样做,因为一次使用一个函数会更加一致。在这种情况下,我编辑了我的答案以添加指向可能解决方案的链接。 对于它的价值,你绝对应该在我的回答中使用该方法,而应该使用 Promise。 Promise 已经解决了这个问题并提供了投掷安全性。你可以简单地省略promise.catch,事实上,上面的整个代码都可以重构为Promise.reject("oh no")(尽管你应该总是有错误拒绝)。然后你可以做Promise.reject("oh no").catch(function(e) console.log(e); ); 记录“哦不”。实际上,除了可链接性和可组合性之外,Throw 安全性是 Promise 的最大卖点之一。 @BenjaminGruenbaum:我假设这只是一个基于其他代码的模型示例,有时(重新)抛出错误,而不是确切的代码。无论如何,我已经编辑了我的答案,以更加强调使用承诺。【参考方案2】:

使用新的async/await syntax,您可以实现这一目标。请注意,在撰写本文时,并非所有浏览器都支持此功能,您可能需要使用 babel(或类似名称)转译您的代码。

// Because of the "async" keyword here, calling getSomeValue()
// will return a promise.
async function getSomeValue() 
  if (somethingIsNotOk) 
    throw new Error('uh oh');
   else 
    return 'Yay!';
  


async function() 
  try 
    // "await" will wait for the promise to resolve or reject
    // if it rejects, an error will be thrown, which you can
    // catch with a regular try/catch block
    const someValue = await getSomeValue();
    doSomethingWith(someValue);
   catch (error) 
    console.error(error);
  

【讨论】:

如果函数返回 Promise,我们是否需要将该方法声明为异步。 如果你想在该函数中使用await,你只需要 async 关键字。使用 async 关键字将产生一个副作用,即承诺将返回一个承诺。如果你想等待一个函数 X 的结果,它返回一个 promise 开始,使函数 X 异步将没有效果。 缺失:声明 Promise.reject(err); 内部 catch ***.com/a/37993829/4933053 @ItsmeJulian 这取决于你想如何处理它。如果您只想在 catch 中登录,那么您不需要将错误冒泡。另外,如果你想把它冒泡,我想我更喜欢throw err inside catch。【参考方案3】:

不!这完全不可能,因为 Promise 本质上是异步的。当抛出异常时,try-catch 子句将完成执行(并且还没有发明时间旅行)。

相反,从所有函数返回 Promise,并在它们上挂钩错误处理程序。

【讨论】:

【参考方案4】:

我经常发现需要确保返回 Promise,并且几乎同样需要处理本地错误,然后选择性地重新抛出它。

function doSomeWork() 
  return Promise.try(function() 

    return request.get(url).then(function(response) 
      // ... do some specific work
    );

  ).catch(function(err) 
    console.log("Some specific work failed", err);
    throw err; // IMPORTANT! throw unless you intend to suppress the error
  );

这种技术 (Promise.try/catch) 的好处是,您可以在没有解决/拒绝要求的情况下启动/确保 Promise 链,这很容易被忽略并造成调试噩梦。 p>

【讨论】:

【参考方案5】:

要扩展edo 的答案,如果您想捕获不想等待的异步函数的错误。您可以在函数末尾添加等待语句。

(async function() 
  try 
    const asyncResult = someAsyncAction();

    // "await" will wait for the promise to resolve or reject
    // if it rejects, an error will be thrown, which you can
    // catch with a regular try/catch block
    const someValue = await getSomeValue();
    doSomethingWith(someValue);

    await asyncResult;
   catch (error) 
    console.error(error);
  
)();

如果someAsyncAction 失败,catch 语句将处理它。

【讨论】:

请注意:这仍然会停止外部函数的完成,直到 asyncResult 完成(出现错误或成功)。如果您根本不想等待 asyncResult,则在 someAsyncAction() 上链接 .catch 可能更容易。 我不确定你说它会停止外部函数的完成是什么意思,但它不会阻塞事件循环。正如它所写的那样,任何放置在外部函数之后的代码的执行都不会被函数内部的 Promise 解析所阻塞。通过将.catch 添加到someAsyncAction(),您将获得的唯一好处是错误处理将被同步绑定。如果someAsyncAction 在到达await asyncResult 行之前被拒绝,这将避免无关的UnhandledPromiseRejectionWarning 消息。 我的意思是,如果某些代码是 await-ing 外部函数,那么该代码仍然需要等待 someAsyncAction 才能完成。很多时候,人们可能想要触发一些异步操作,但不等待解决/错误的承诺,例如发送分析数据。现在,当链接.catch(并完全省略await asyncResult)时,您仍然可以处理someAsyncAction 上的异步错误,同时确保没有其他逻辑在等待它。

以上是关于用一级 try ... catch 捕获 JavaScript Promise 中的错误的主要内容,如果未能解决你的问题,请参考以下文章

php中,用try/catch捕获了异常,为啥还会有警告?有没有办法去掉呢?

JAVA中try catch捕获异常的问题

Java:简述try-catch-finally异常捕获

Java:简述try-catch-finally异常捕获

java中 try-catch语句

测试 __try, __finally, __except(被__finally捕获的异常, 还会被上一级的__except捕获。反之不行)