如何重试一个xhr请求,该请求在状态0上至少递归n次递归

时间:2016-07-22 07:18:44

标签: javascript ajax xmlhttprequest promise q

我写了下面这段代码。 makeRequest被调用,我想在xhr状态为0时重试此问题。问题是我无法解析正确的promise,重试逻辑在第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;
};

1 个答案:

答案 0 :(得分:1)

以下是我接近它的方法(参见***评论):

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

重新评论:

  

这确实解决了我当前的问题,但是有没有办法解决添加到调用堆栈的所有承诺,如果其中任何一个被解析?

是的:要使用Angular的$q(我假设你正在使用的是),你可以将你从延迟对象的递归调用中获得的承诺传递给resolve:由于这是一个承诺,延期将等待它根据承诺的作用得到解决和解决或拒绝。如果您在链中的每个级别执行此操作,则解决方案会在链中向上运行:

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