异步失败时更喜欢抛出或拒绝

Prefer throw or reject when failing promise asynchronously

我有一个 Bluebird 承诺,它包装了一个 AJAX 请求,并且需要在请求失败时拒绝该承诺。我想向可能附加的任何 catch 块提供请求失败的原因,主要来自状态代码。为了实现这一点,我有 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();
    });
  }
}

Promise 构造函数内部

promise 构造函数是抛出安全的,但本质上您通常不会在内部处理抛出安全的东西 - 因此例如以下内容不安全:

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

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

then 处理程序中

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

除了 Angular 1.x 的 $q 之外,这对于任何承诺实现都是正确的,它区分了两者 - 但它是奇怪的球(当你 throw 那里即使您处理了错误,它也会记录)。

在你的代码中

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

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