【问题标题】:AngularJS: cancel an $http request using timeout propertyAngularJS:使用超时属性取消 $http 请求
【发布时间】:2015-08-15 23:29:54
【问题描述】:

我已经看到很多关于这个主题的帖子,以及很多网络文章,但我仍然被我的问题所困扰。我发现this post 非常有用,但我的 timeoutRequest() 函数永远不会被调用。

我正在使用具有 $http 超时属性的承诺,但基础 HTTP 请求没有被取消。我认为承诺本身正在解决,但请求并未取消。

我的控制器行为如下所示:

$scope.enquiriesSelected = function() {
    $scope.cancelHttpRequests();
    $location.path("/enquiries");
};

$scope.cancelHttpRequests = function () {
    console.log(canceller.promise.$$state);
    canceller.resolve("User cancelled");
    console.log(canceller.promise.$$state);
};

我的 HTTP 请求承诺如下所示:

var canceller = $q.defer();

$scope.searchResultsPromise = $http({
        url: "/api/customers/customersearch",
        method: "POST",
        data: criteria,
        timeout: canceller.promise 
    })
    .success(function(data) {
        $scope.customerSearchResults = data;
    });

我已经尝试了各种方法来让它工作,包括将取消器放在 $scope 中。

我一直在查看 AngularJS 源代码,发现以下几行:

if (timeout > 0) {
    var timeoutId = $browserDefer(timeoutRequest, timeout);
} else if (isPromiseLike(timeout)) {
    timeout.then(timeoutRequest);
}

function timeoutRequest() {
    jsonpDone && jsonpDone();
    xhr && xhr.abort();
}

但是,当我的承诺得到解决时,执行路径没有到达这些行。 xhr.abort() 永远不会被调用,这是 AngularJS 源代码中唯一中止 HTTP 请求的地方。

在尝试取消请求时使用 F12 (Chrome) 中的控制台进行检查会发现,promise 的 $$state 从 0 变为 1,因此我有理由确定 promise 正在解决。然而,在网络流量中,HTTP 请求并没有被取消。在 HTTP 请求完成之前,我无法导航到另一个页面。

谁能帮忙?

M

【问题讨论】:

  • 如果您的请求需要很长时间,我可能会在服务器端创建一个布尔值,表示数据已加载/准备好,如果布尔值为真,您可以每秒检查一次,取回数据。这里有两个优点:用户可以取消计时器并避免检查数据准备好,浏览器不会挂起。
  • 恐怕我做不到。我正在调用一个我没有源代码的服务。
  • 为什么要将 promise 保存到 $scope.searchResultsPromise ?如果你删除它,有帮助吗?
  • 这里的问题理论上相当简单,但实现起来却非常复杂,想象一下客户端服务器架构。一旦客户端完成发送请求,服务器将超时该请求。因此,您必须在服务器端对其进行编码以允许以超时毫秒为值的可选参数,如果设置了该请求处理所需的时间超过超时,则服务器将发送超时错误。但即使在这种情况下,也没有考虑到通信时间的流逝。客户端设置的超时只返回错误的承诺而不处理响应
  • 为了缩小这个范围,你能不能在一个完全简单的 Angular 应用程序中重新创建这个问题,只有一个控制器,只有这个 http 请求被触发,然后发布代码 + 一个链接到它工作(/不工作)某处?

标签: javascript angularjs


【解决方案1】:

您所描述的实际上是使用timeout 属性取消$http 请求的正确方法。我创建了this Plunkr,它展示了这种行为与您提供的代码尽可能接近。您可以看到,当单击“Submit request with timeout”按钮时,会提交请求,并且在 100 毫秒后解决超时承诺(因为请求返回的速度如此之快,以至于您真的没有时间单击某些内容来取消它) :

$scope.requestWithTimeout = function() {
  $timeout(function(){canceller.resolve("User cancelled");}, 100);
  $scope.submitRequest();
}

$http 请求错误回调检查响应的状态并显示适当的错误消息,您可以在其中看到请求实际上在 100 毫秒后被取消。您还可以通过观察浏览器开发者工具的网络选项卡中的请求来验证这一点。

$scope.submitRequest = function() {
    // Reset the canceler promise
    canceller = $q.defer();
    $scope.status = 'Request submitted';
    var startTime = (new Date()).getTime();
    $http({ 
      url: 'style.css',
      method: 'GET',
      timeout: canceller.promise 
    })
    .success(function(data) {
      var endTime = (new Date()).getTime();
      $scope.status = 'Request response returned after ' + (endTime - startTime) + ' msec.';
    })
    .error(function(data, status) {
      var endTime = (new Date()).getTime();
      if(status === 0) {
        $scope.status = 'Request timed out after ' + (endTime - startTime) + ' msec.';
      } else {
        $scope.status = 'Request returned with error after ' + (endTime - startTime) + ' msec.';
      }
    });
  }

希望您可以以此为例来找出您的代码有什么问题并加以纠正。否则,请使用不起作用的代码创建一个 Plunkr(或类似的东西),以便我可以进一步帮助您。

【讨论】:

  • 好的,我将创建一个 Plunkr 并在此处发布 URL。谢谢。
  • 这很难,因为HTTP请求返回很快;我正在寻找一个长期运行的服务电话。
  • 好的,克里斯蒂娜,我已经在plnkr.co/edit/zhhYjyIZdNdUjYxHFn5U 上分叉了你的 Plunkr。这不是最漂亮的,但它确实证明了它确实有效,所以我必须弄清楚我的代码和这个沙箱代码之间有什么不同。
  • @serlingpa 你确定它有效吗?从我可以看到您尝试通过$http 请求访问的资源对于 plunkr 来说是不可访问的(由于 CORS 限制),因此在用户有机会取消它之前请求失败。当然,这并不是说如果资源可以访问它就不会工作,只是在这种特殊情况下,请求会因为其他原因而失败。
  • 嗯...你是对的。但是网络选项卡显示的状态是取消而不是完成,所以我希望它可以工作!我似乎无法在网上找到避免 CORS 的大型 PDF。
猜你喜欢
  • 2014-05-19
  • 1970-01-01
  • 2018-01-22
  • 2019-09-12
  • 2013-05-07
  • 1970-01-01
相关资源
最近更新 更多