【问题标题】:Prefer throw or reject when failing promise asynchronously [duplicate]异步失败承诺时首选抛出或拒绝[重复]
【发布时间】:2015-08-12 11:14:01
【问题描述】:

我有一个包装 AJAX 请求的 Bluebird 承诺,并且需要在请求失败时拒绝该承诺。我想提供请求失败的原因,主要来自状态代码,可能附加的任何捕获块。为了实现这一点,我有 UnauthorizedErrorNotFoundError 以及类似的类,它们都扩展了 Error 以使用 Bluebird 的模式匹配 catch

我不确定的部分是我应该throw 还是调用拒绝处理程序。我的代码看起来像:

class Request {
  // other methods

  send(method, url, args, body) {
    return new Promise((res, rej) => {
      let xhr = new XMLHttpRequest();
      xhr.open(method, url);

      xhr.onload = () => {
        res(JSON.parse(xhr.responseText));
      };

      xhr.onerror = () => {
        let status = xhr.status;
        switch (status) {
          case 401:
            // Should I use throw:
            throw new UnauthorizedError(url);
            // or
            rej(new UnauthorizedError(url));
        }
      };

      xhr.send();
    });
  }
}

【问题讨论】:

    标签: javascript error-handling promise ecmascript-6 bluebird


    【解决方案1】:

    Promise 构造函数内部

    promise 构造函数是 throw safe,但本质上你通常不会处理 throw safe 里面的东西 - 所以例如以下是不安全的:

    new Promise(function(resolve, reject){
         setTimeout(function(){
             // NEVER throw here, it'll throw globally and not reject the promise
         }, 100);
    });
    

    promise 构造函数通常仅用于将回调 API 转换为 Promise,并且由于回调不像 Promise 那样是 throw 安全的,因此当它们异步出错(而不是 throw)时您必须拒绝。

    then 处理程序中

    两者在功能上是相同的。当您从 then 处理程序中 throw 时,您将返回一个被拒绝的承诺。我更喜欢抛出,因为发生错误比return 更明确,但这并不重要。

    除了 Angular 1.x 的 $q 区分这两者之外的任何承诺实现都是如此 - 但这是一个奇怪的球(当你 throw 出现时,即使你处理了错误,它也会记录下来)。

    在您的代码中

    在您的代码中,您拒绝和处理承诺的方式存在一些错误。 Promise 非常健壮,因为它们可以优雅地为您处理错误 - 在这方面,当您将回调 API 转换为 Promise 时,您必须非常小心:

    class Request {
        // other methods
    
        send(method, url, args, body) {
            return new Promise((res, rej) => {  // it's good that new Promise is the first
                let xhr = new XMLHttpRequest(); // line since it's throw-safe. This is why it
                xhr.open(method, url);          // was chosen for the API and not deferreds
    
                xhr.onload = () => {
                    // This _needs_ a try/catch, it will fail if responseText
                    // is invalid JSON and will throw to the global scope instead of rejecting
                    res(JSON.parse(xhr.responseText));
                };
    
                xhr.onerror = () => {
                    let status = xhr.status;
                    switch (status) {
                    case 401:
                        // this _must_ be a reject, it should also generally be surrounded
                        // with a try/catch
                        rej(new UnauthorizedError(url));
                    }
                };
    
                xhr.send(); // it's important that this is in the promise constructor
            });
        }
    }
    

    【讨论】:

    • JSON.parse 的 try/catch 中应该包含什么内容? reject(new ParseError());?
    • 为什么我们需要用try..catch包围拒绝处理程序?
    • 好吧,xhr.onerror 可能会异步执行——此时它不在 Promise 构造函数中执行,因此它不可能捕获抛出的异常。这与setTimeout 示例完全相同——您无法在您无法控制的异步函数中捕获错误。您必须手动将异常转换为拒绝,这通常是 Promise 库为您所做的。
    • 另一种选择是立即解决 onload 并拒绝 onerror,然后使用.then(xhr => JSON.parse(xhr.responseText), err => { if (status === 401) throw new UnauthorusedError(url); })。然后你不必担心自己做try/catch。只需确保您在异步回调中执行的唯一操作是调用 resolvereject
    猜你喜欢
    • 2019-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-11
    • 1970-01-01
    • 2019-05-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多