【问题标题】:Catching Errors in JavaScript Promises with a First Level try ... catch用一级 try ... catch 捕获 JavaScript Promise 中的错误
【发布时间】: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。我相信你可以让它工作,但为什么要让你的代码那样混乱呢?

标签: javascript node.js promise bluebird


【解决方案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 stackoverflow.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 上的异步错误,同时确保没有其他逻辑在等待它。
      猜你喜欢
      • 2022-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-12
      • 1970-01-01
      • 2016-05-21
      • 2014-03-04
      • 1970-01-01
      相关资源
      最近更新 更多