本文介绍了如何重试 xhr 请求,该请求在状态 0 上至少递归返回 n 次承诺的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写了以下代码.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);//设置标题无功反应;xhr.onload = 函数(){if (xhr.status === 200 && xhr.readyState === 4 && xhr.getResponseHeader('content-type') !=='文本/html') {尝试 {响应 = JSON.parse(xhr.response);deferred.resolve(响应);}赶上(e){deferred.reject(e);}} else if (xhr.status === 0) {//在这里重试;deferred.resolve(makeRequest(method, urlToBeCalled, payload));} 别的 {尝试 {响应 = JSON.parse(xhr.response);deferred.reject(response);}赶上(e){deferred.reject(xhr.response);}}};xhr.onerror = 函数(){deferred.reject(xhr.response);};xhr.send(payload);返回 deferred.promise;};
解决方案

这是我的处理方式(见 *** 评论):

var makeRequest = function(method, urlToBeCalled, payload) {var deferred = $q.defer();var 重试 = 4;//*** 柜台跑步();//*** 调用工人返回 deferred.promise;//*** 将实际工作移到自己的函数中函数运行(){var xhr = new XMLHttpRequest();xhr.open(method, encodeURI(urlToBeCalled), true);setHttpRequestHeaders(xhr);xhr.onload = 函数(){if (xhr.status === 200 && xhr.readyState === 4 && xhr.getResponseHeader('content-type') !== 'text/html') {尝试 {响应 = JSON.parse(xhr.response);deferred.resolve(响应);}赶上(e){deferred.reject(e);}} else if (xhr.status === 0) {//重试if (retries--) {//*** 如果还有重试,则递归跑步();} 别的 {//*** 重试失败deferred.reject(e);}} 别的 {//*** 见下面的注释,可能删除这个尝试 {响应 = JSON.parse(xhr.response);deferred.reject(response);}赶上(e){deferred.reject(xhr.response);}}};xhr.onerror = 函数(){deferred.reject(xhr.response);};xhr.send(payload);}};

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

xhr.onload = function() {如果(xhr.readyState === 4){//完成了,发生了什么?如果(xhr.status === 200){if (xhr.getResponseHeader('content-type') !== 'text/html') {尝试 {响应 = JSON.parse(xhr.response);deferred.resolve(响应);}赶上(e){deferred.reject(e);}} 别的 {//出问题了?deferred.reject(e);}} else if (xhr.status === 0) {//重试if (retries--) {//*** 如果还有重试,则递归跑步();} 别的 {//*** 重试失败deferred.reject(e);}}}};

重新评论:

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

是的:要使用 Angular 的 $q(我假设这就是您正在使用的)来做到这一点,您只需将从递归调用中获得的承诺传递给 resolve on your deferred object:因为它是一个promise,deferred将等待它被解决,并根据promise所做的来解决或拒绝.如果您在链的每个级别都这样做,则解决方案会沿着链向上发展:

angular.module("mainModule", []).controller("主控制器",函数($scope,$q,$http){测试(真).然后(功能(){测试(假);});功能测试(标志){日志(标志?测试已解决":测试被拒绝");返回递归(3,标志).then(函数(参数){log("已解决", arg);}).catch(函数(参数){log("被拒绝", arg);});}函数递归(计数,标志){log("recursive(" + count + ", " + flag + ") 调用");var d = $q.defer();设置超时(函数(){如果(计数 
pre {边距:0;填充:0;}

<div ng-controller="mainController"></div>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

你可以用 JavaScript 自己的 promise 来做同样的事情:

test(true).then(function() {测试(假);});功能测试(标志){日志(标志?测试已解决":测试被拒绝");返回递归(3,标志).then(函数(参数){log("已解决", arg);}).catch(函数(参数){log("被拒绝", arg);});}函数递归(计数,标志){log("recursive(" + count + ", " + flag + ") 调用");返回新的承诺(功能(解决,拒绝){设置超时(函数(){如果(计数 
pre {边距:0;填充:0;}

I have written the below piece of code. makeRequest gets called and I want to retry this when xhr status is 0. The problem is I am not able to resolve the correct promise, the retry logic fetches the correct response in nth attempt but fails to propagate to the caller method.

How do I resolve this issue.

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;
};
解决方案

Here's how I'd approach it (see *** comments):

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


Side note: The content of your initial if body and the final else appear to be identical. I think I'd recast the entire 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);
            }
        }
    }
};


Re your comment:

Yes: To do that with Angular's $q (I assume that's what you're using), you can just pass the promise you get back from the recursive call into resolve on your deferred object: Since it's a promise, the deferred will wait for it to be settled and resolve or reject based on what that promise does. If you do this at every level in the chain, the resolutions work their way up the chain:

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>

You can do the same thing with JavaScript's own promises:

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

这篇关于如何重试 xhr 请求,该请求在状态 0 上至少递归返回 n 次承诺的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-05 13:15