15

我有一个“可取消”的 angularJs$http调用,如下所示:

var defer = $q.defer()
$http.post("http://example.com/someUrl", {some: "data"}, {timeout: defer.promise})

我取消了那个 ajax 请求,defer.resolve()因为某些逻辑需要它。


在我的代码中的其他地方,我有一个这样的迭代器:

angular.module("services.interceptor", arguments).config(function($httpProvider) {
     $httpProvider.interceptors.push(function($q) {
        return {
          responseError: function(rejection) {
            if(rejection.status == 0) {
              alert("check your connection");
            }
           return $q.reject(rejection);
        }
      };
    });
  });
});

问题:

如果存在 Internet连接问题ajax 会以状态 0 失败,并且拦截器会捕获它。如果 ajax 被超时 promise取消,那么status 也是 0并且拦截器会捕获它。

我无法确定它是否被取消或responseError处理程序出错。

我天真的方法是检查超时是否定义如下:

responseError: function(rejection) {
  if(rejection.status == 0 && !rejection.config.timeout) {
    alert("check your connection");
  }
  return $q.reject(rejection);
}

它仅保证该请求存在超时条件,而不是因此而失败。但总比没有好。

是否有一种真正有效的方法来确定 ajax 是否失败或取消?

我正在使用 AngularJs 1.1.5

4

5 回答 5

5

我最终使用一个标志来执行此操作,该标志在错误处理程序中进行检查。

我开始遵循这个 AngularJS Github 问题末尾的建议:https ://github.com/angular/angular.js/issues/1159#issuecomment-25735438

这导致我构建了一项服务,该服务使用承诺手动“超时”http调用 - 正如您在下面所做的那样 - 通过复制该问题的链接 plunk:http ://plnkr.co/edit/P8wKns51GVqw5mwS5l5R?p=preview

但这并不完全,因为这并没有解决您提出的问题;在错误处理中,如何确定调用是真正的服务器中断/超时,还是仅仅是手动触发的超时。最后,我不得不求助于将它存储在另一个变量中(类似于超时承诺),这可以在这个 plunk 中看到:http: //plnkr.co/edit/BW6Zwu

这段代码的主要方法如下

return function (url) {
    var cancelQuery = null;
    var queryCancelled = false;

    return function runQuery(query) {
      if (cancelQuery) {
        queryCancelled = true;
        cancelQuery.resolve();
      }
      cancelQuery = $q.defer();
      return $http.
        get(url, { params: { query: query }, timeout: cancelQuery.promise }).
        then(function (response) {
          cancelQuery = null;
          return response.data;
        }, function (error) {
          if(queryCancelled) {
            console.log("Request cancelled");
            queryCancelled = false;
          } else {
            console.log("Actual Error !");
          }
        });
    };
  };

不太优雅,但它似乎有效,并且避免了我观察到的任何令人讨厌的竞争条件。

于 2013-11-13T04:58:39.797 回答
4

如果您使用 中止请求timeout,则超时将在响应配置​​中列出。

检查.config.timeout您的响应对象:

function config($httpProvider) {

    $httpProvider.interceptors.push(function ($q, $injector) {
        return {
            request: function (config) {
                return config;
            },

            response: function (response) {
                return response || $q.when(response);
            },

            responseError: function (response) {
                switch (response.status) {
                    case -1:
                    case 500 :
                    case 501 :
                    case 502 :
                    case 503 :
                    case 504 :
                        // the webapp sometimes aborts network requests. Those that dropped should not be restarted
                        if (response.status === -1 && response.config.timeout) {
                            break;
                        }
                        return retryRequest(response.config, response.status);
                }
                // otherwise
                return $q.reject(response);
            }
        };
    });
}
于 2015-11-25T18:23:04.330 回答
0

我知道这个问题已经有点老了,但我发现它正在寻找解决同一问题的方法。除了已经给出的答案之外,我不想检查我在取消 AJAX 请求的同时设置的标志。它可能已经是一个未取消的新请求。

所以我为我找到了一个不同的解决方案。也许有人可以利用它,尽管这不是一个最新的问题。

我的解决方案的精髓是,不仅要检查状态。如果数据也为空,则它是一个已取消的请求。

var defer = $q.defer()
$http.get("http://example.com/someUrl", {timeout: defer.promise})
  .error(function(data, status){
      if (data === null && status === 0) {
          // the request was cancled
      }
   });
于 2014-09-04T14:16:43.920 回答
0

您可以使用(rejection.config.timeout.status != 499):

...timeout.promise.status = 499;
...timeout.resolve();

responseError: function(rejection) {
  if(rejection.status == 0 && rejection.config.timeout.status != 499) {
    alert("check your connection");
  }
  return $q.reject(rejection);
}

代替:

responseError: function(rejection) {
  if(rejection.status == 0 && !rejection.config.timeout) {
    alert("check your connection");
  }
  return $q.reject(rejection);
}

顺便说一句,在这种情况下,您不需要额外的请求装饰器。

于 2017-01-28T14:46:22.147 回答
0

我可以通过检查全局事件对象的属性类型与字符串“abort”(下面的代码)来确定请求是否在 Chrome v55 中被取消。我遇到的问题是,当导航到另一个页面并触发 responseError 时,正在进行的请求被取消。

angular.module("services.interceptor", arguments)
  .config(function($httpProvider) {
     $httpProvider.interceptors.push(function($q, $window) {
        return {
          responseError: function(rejection) {
            if($window.event.type === "abort") {
               // handle cancelled here
            } else if(rejection.status < 1) {
              alert("check your connection");
            }
           return $q.reject(rejection);
        }
      };
    });
  });
});

(另外:由于网络错误,status 属性似乎已从 0 更改为 -1,所以我写了rejection.status < 1))

于 2017-01-31T12:14:43.070 回答