如何在状态 0 上重试一个 returns 递归承诺至少 n 次的 xhr 请求

How to retry an xhr request which returns a promise recursively for atleast n times on status 0

我写了下面的一段代码。 makeRequest 被调用,我想在 xhr 状态为 0 时重试。问题是我无法解决正确的承诺,重试逻辑在第 n 次尝试中获取正确的响应,但未能传播到调用方方法.

我该如何解决这个问题。

var makeRequest = function(method, urlToBeCalled, payload) {
  var deferred = $q.defer();
  var xhr = new XMLHttpRequest();
  xhr.open(method, encodeURI(urlToBeCalled), true);
  setHttpRequestHeaders(xhr); // set headers
  var response;
  xhr.onload = function() {
    if (xhr.status === 200 && xhr.readyState === 4 && xhr.getResponseHeader('content-type') !==
      'text/html') {
      try {
        response = JSON.parse(xhr.response);
        deferred.resolve(response);
      } catch (e) {
        deferred.reject(e);
      }
    } else if (xhr.status === 0) {
      // retry here;
      deferred.resolve(makeRequest(method, urlToBeCalled, payload));
    } else {
      try {
        response = JSON.parse(xhr.response);
        deferred.reject(response);
      } catch (e) {
        deferred.reject(xhr.response);
      }
    }
  };
  xhr.onerror = function() {
    deferred.reject(xhr.response);
  };
  xhr.send(payload);
  return deferred.promise;
};

以下是我的处理方法(参见 *** 评论):

var makeRequest = function(method, urlToBeCalled, payload) {
    var deferred = $q.defer();
    var retries = 4;                     // *** Counter
    run();                               // *** Call the worker
    return deferred.promise;

    // *** Move the actual work to its own function
    function run() {
        var xhr = new XMLHttpRequest();
        xhr.open(method, encodeURI(urlToBeCalled), true);
        setHttpRequestHeaders(xhr);
        xhr.onload = function() {
            if (xhr.status === 200 && xhr.readyState === 4 && xhr.getResponseHeader('content-type') !== 'text/html') {
                try {
                    response = JSON.parse(xhr.response);
                    deferred.resolve(response);
                } catch (e) {
                    deferred.reject(e);
                }
            } else if (xhr.status === 0) {
                // retry
                if (retries--) {          // *** Recurse if we still have retries 
                    run();
                } else {
                    // *** Out of retries
                    deferred.reject(e);
                }
            } else {
                // *** See note below, probably remove this
                try {
                    response = JSON.parse(xhr.response);
                    deferred.reject(response);
                } catch (e) {
                    deferred.reject(xhr.response);
                }
            }
        };
        xhr.onerror = function() {
            deferred.reject(xhr.response);
        };
        xhr.send(payload);
    }
};

旁注:您的初始 if 正文和最终 else 的内容似乎相同。我想我会重铸整个 onload:

xhr.onload = function() {
    if (xhr.readyState === 4) {
        // It's done, what happened?
        if (xhr.status === 200) {
            if (xhr.getResponseHeader('content-type') !== 'text/html') {
                try {
                    response = JSON.parse(xhr.response);
                    deferred.resolve(response);
                } catch (e) {
                    deferred.reject(e);
                }
            } else {
                // Something went wrong?
                deferred.reject(e);
            }
        } else if (xhr.status === 0) {
            // retry
            if (retries--) {          // *** Recurse if we still have retries 
                run();
            } else {
                // *** Out of retries
                deferred.reject(e);
            }
        }
    }
};

回复您的评论:

This does resolve my current problem but is there a way to resolve all the promises which are added to call stack if any one of those is resolved?

是的:要使用 Angular 的 $q 来做到这一点(我假设这就是您正在使用的),您可以将递归调用返回的承诺传递给 resolve 在你的 deferred 对象上:因为它是一个承诺,deferred 将等待它被解决并根据承诺的内容解决或拒绝。如果您在链中的每个级别都执行此操作,则解决方案会在链中向上移动:

angular.module("mainModule", []).controller(
  "mainController",
  function($scope, $q, $http) {
    test(true).then(function() {
      test(false);
    });

    function test(flag) {
      log(flag ? "Testing resolved" : "Testing rejected");
      return recursive(3, flag)
        .then(function(arg) {
          log("Resolved with", arg);
        })
        .catch(function(arg) {
          log("Rejected with", arg);
        });
    }

    function recursive(count, flag) {
      log("recursive(" + count + ", " + flag + ") called");
      var d = $q.defer();
      setTimeout(function() {
        if (count <= 0) {
          // Done, settle
          if (flag) {
            log("Done, resolving with " + count);
            d.resolve(count);
          } else {
            log("Done, rejecting with " + count);
            d.reject(count);
          }
        } else {
          // Not done, resolve with promise from recursive call
          log("Not done yet, recursing with " + (count - 1));
          d.resolve(recursive(count - 1, flag));
        }
      }, 0);
      return d.promise;
    }
  }
);

function log() {
  var p = document.createElement('pre');
  p.appendChild(
    document.createTextNode(
      Array.prototype.join.call(arguments, " ")
    )
  );
  document.body.appendChild(p);
}
pre {
  margin: 0;
  padding: 0;
}
<div ng-app="mainModule">
  <div ng-controller="mainController"></div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

你可以用 JavaScript 自己的承诺做同样的事情:

test(true).then(function() {
  test(false);
});

function test(flag) {
  log(flag ? "Testing resolved" : "Testing rejected");
  return recursive(3, flag)
    .then(function(arg) {
      log("Resolved with", arg);
    })
    .catch(function(arg) {
      log("Rejected with", arg);
    });
}

function recursive(count, flag) {
  log("recursive(" + count + ", " + flag + ") called");
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      if (count <= 0) {
        // Done, resolve with value
        if (flag) {
          log("Done, resolving with " + count);
          resolve(count);
        } else {
          log("Done, rejecting with " + count);
          reject(count);
        }
      } else {
        // Not done, resolve with promise
        // from recursive call
        log("Not done yet, recursing with " + (count - 1));
        resolve(recursive(count - 1, flag));
      }
    }, 0);
  });
}

function log() {
  var p = document.createElement('pre');
  p.appendChild(
    document.createTextNode(
      Array.prototype.join.call(arguments, " ")
    )
  );
  document.body.appendChild(p);
}
pre {
  margin: 0;
  padding: 0;
}