【问题标题】:How to retry an xhr request which returns a promise recursively for atleast n times on status 0如何重试 xhr 请求,该请求在状态 0 上递归返回至少 n 次承诺
【发布时间】:2016-11-26 00:04:02
【问题描述】:

我已经编写了下面的代码。 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;
};

【问题讨论】:

    标签: javascript ajax xmlhttprequest promise q


    【解决方案1】:

    这是我的处理方式(请参阅*** cmets):

    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:因为这是一个承诺, 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 自己的 Promise 做同样的事情:

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

    【讨论】:

    • 噢!如果您在上方没有看到“重试次数不足”,请点击刷新。
    • 谢谢。这确实解决了我当前的问题,但是如果其中任何一个得到解决,有没有办法解决添加到调用堆栈的所有承诺?
    • @arbghl:是的——这实际上很简单,你只需用一个承诺调用resolve,你调用它的承诺将根据该承诺解决或拒绝。我在上面添加了示例。
    猜你喜欢
    • 1970-01-01
    • 2017-05-15
    • 1970-01-01
    • 2017-02-22
    • 2014-10-31
    • 2018-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多