如何在AngularJS中取消$http请求?

如何在AngularJS中取消$http请求?,angularjs,promise,angular-promise,angular-http,cancellation,Angularjs,Promise,Angular Promise,Angular Http,Cancellation,在AngularJS中给出了一个Ajax请求 $http.get("/backend/").success(callback); 如果在同一后端启动另一个请求,例如不同的参数,取消该请求的最有效方法是什么。当前版本的AngularJS不支持取消使用$http发出的请求。有一种方法可以添加此功能,但此PR尚未审核,因此不清楚它是否会进入AngularJS core。此功能是通过超时参数实现的: var canceler = $q.defer(); $http.get('/someUrl', {t

在AngularJS中给出了一个Ajax请求

$http.get("/backend/").success(callback);

如果在同一后端启动另一个请求,例如不同的参数,取消该请求的最有效方法是什么。

当前版本的AngularJS不支持取消使用$http发出的请求。有一种方法可以添加此功能,但此PR尚未审核,因此不清楚它是否会进入AngularJS core。

此功能是通过超时参数实现的:

var canceler = $q.defer();
$http.get('/someUrl', {timeout: canceler.promise}).success(successCallback);
// later...
canceler.resolve();  // Aborts the $http request if it isn't finished.

在Angular 1.3.15中,使用timeout属性取消Angular$http Ajax不起作用。 对于那些迫不及待要解决这个问题的人,我将分享一个用Angular包装的jQueryAjax解决方案

该解决方案涉及两项服务:

HttpService是jQueryAjax函数的包装器; PendingRequestsService跟踪挂起/打开的Ajax请求 下面是PendingRequestsService服务:

    (function (angular) {
    'use strict';
    var app = angular.module('app');
    app.service('PendingRequestsService', ["$log", function ($log) {            
        var $this = this;
        var pending = [];
        $this.add = function (request) {
            pending.push(request);
        };
        $this.remove = function (request) {
            pending = _.filter(pending, function (p) {
                return p.url !== request;
            });
        };
        $this.cancelAll = function () {
            angular.forEach(pending, function (p) {
                p.xhr.abort();
                p.deferred.reject();
            });
            pending.length = 0;
        };
    }]);})(window.angular);
     (function (angular) {
        'use strict';
        var app = angular.module('app');
        app.service('HttpService', ['$http', '$q', "$log", 'PendingRequestsService', function ($http, $q, $log, pendingRequests) {
            this.post = function (url, params) {
                var deferred = $q.defer();
                var xhr = $.ASI.callMethod({
                    url: url,
                    data: params,
                    error: function() {
                        $log.log("ajax error");
                    }
                });
                pendingRequests.add({
                    url: url,
                    xhr: xhr,
                    deferred: deferred
                });            
                xhr.done(function (data, textStatus, jqXhr) {                                    
                        deferred.resolve(data);
                    })
                    .fail(function (jqXhr, textStatus, errorThrown) {
                        deferred.reject(errorThrown);
                    }).always(function (dataOrjqXhr, textStatus, jqXhrErrorThrown) {
                        //Once a request has failed or succeeded, remove it from the pending list
                        pendingRequests.remove(url);
                    });
                return deferred.promise;
            }
        }]);
    })(window.angular);
HttpService服务:

    (function (angular) {
    'use strict';
    var app = angular.module('app');
    app.service('PendingRequestsService', ["$log", function ($log) {            
        var $this = this;
        var pending = [];
        $this.add = function (request) {
            pending.push(request);
        };
        $this.remove = function (request) {
            pending = _.filter(pending, function (p) {
                return p.url !== request;
            });
        };
        $this.cancelAll = function () {
            angular.forEach(pending, function (p) {
                p.xhr.abort();
                p.deferred.reject();
            });
            pending.length = 0;
        };
    }]);})(window.angular);
     (function (angular) {
        'use strict';
        var app = angular.module('app');
        app.service('HttpService', ['$http', '$q', "$log", 'PendingRequestsService', function ($http, $q, $log, pendingRequests) {
            this.post = function (url, params) {
                var deferred = $q.defer();
                var xhr = $.ASI.callMethod({
                    url: url,
                    data: params,
                    error: function() {
                        $log.log("ajax error");
                    }
                });
                pendingRequests.add({
                    url: url,
                    xhr: xhr,
                    deferred: deferred
                });            
                xhr.done(function (data, textStatus, jqXhr) {                                    
                        deferred.resolve(data);
                    })
                    .fail(function (jqXhr, textStatus, errorThrown) {
                        deferred.reject(errorThrown);
                    }).always(function (dataOrjqXhr, textStatus, jqXhrErrorThrown) {
                        //Once a request has failed or succeeded, remove it from the pending list
                        pendingRequests.remove(url);
                    });
                return deferred.promise;
            }
        }]);
    })(window.angular);
稍后在服务中加载数据时,您将使用HttpService而不是$http:

(function (angular) {

    angular.module('app').service('dataService', ["HttpService", function (httpService) {

        this.getResources = function (params) {

            return httpService.post('/serverMethod', { param: params });

        };
    }]);

})(window.angular);
稍后在代码中,您希望加载数据:

(function (angular) {

var app = angular.module('app');

app.controller('YourController', ["DataService", "PendingRequestsService", function (httpService, pendingRequestsService) {

    dataService
    .getResources(params)
    .then(function (data) {    
    // do stuff    
    });    

    ...

    // later that day cancel requests    
    pendingRequestsService.cancelAll();
}]);

})(window.angular);

如果您想取消stateChangeStart with ui router上挂起的请求,可以使用以下方法:

//在职

                var deferred = $q.defer();
                var scope = this;
                $http.get(URL, {timeout : deferred.promise, cancel : deferred}).success(function(data){
                    //do something
                    deferred.resolve(dataUsage);
                }).error(function(){
                    deferred.reject();
                });
                return deferred.promise;
//在UIrouter配置中

$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
    //To cancel pending request when change state
       angular.forEach($http.pendingRequests, function(request) {
          if (request.cancel && request.timeout) {
             request.cancel.resolve();
          }
       });
    });

这通过如下所示的abort方法装饰$http服务来增强可接受的答案

'use strict';
angular.module('admin')
  .config(["$provide", function ($provide) {

$provide.decorator('$http', ["$delegate", "$q", function ($delegate, $q) {
  var getFn = $delegate.get;
  var cancelerMap = {};

  function getCancelerKey(method, url) {
    var formattedMethod = method.toLowerCase();
    var formattedUrl = encodeURI(url).toLowerCase().split("?")[0];
    return formattedMethod + "~" + formattedUrl;
  }

  $delegate.get = function () {
    var cancelerKey, canceler, method;
    var args = [].slice.call(arguments);
    var url = args[0];
    var config = args[1] || {};
    if (config.timeout == null) {
      method = "GET";
      cancelerKey = getCancelerKey(method, url);
      canceler = $q.defer();
      cancelerMap[cancelerKey] = canceler;
      config.timeout = canceler.promise;
      args[1] = config;
    }
    return getFn.apply(null, args);
  };

  $delegate.abort = function (request) {
    console.log("aborting");
    var cancelerKey, canceler;
    cancelerKey = getCancelerKey(request.method, request.url);
    canceler = cancelerMap[cancelerKey];

    if (canceler != null) {
      console.log("aborting", cancelerKey);

      if (request.timeout != null && typeof request.timeout !== "number") {

        canceler.resolve();
        delete cancelerMap[cancelerKey];
      }
    }
  };

  return $delegate;
}]);
  }]);
rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
  angular.forEach($http.pendingRequests, function (request) {
        $http.abort(request);
    });
  });
这段代码在做什么

要取消请求,必须设置承诺超时。 如果HTTP请求没有设置超时,那么代码将添加承诺超时。 如果已设置超时,则不会更改任何内容

然而,为了解决承诺,我们需要处理延迟的问题。 因此,我们使用一个映射,以便稍后检索延迟的数据。 当我们调用abort方法时,将从映射中检索deferred,然后调用resolve方法取消http请求

希望这对别人有帮助

局限性

目前,这只适用于$http.get,但您可以为$http.post等添加代码

如何使用

然后,您可以在状态更改时使用它,如下所示

'use strict';
angular.module('admin')
  .config(["$provide", function ($provide) {

$provide.decorator('$http', ["$delegate", "$q", function ($delegate, $q) {
  var getFn = $delegate.get;
  var cancelerMap = {};

  function getCancelerKey(method, url) {
    var formattedMethod = method.toLowerCase();
    var formattedUrl = encodeURI(url).toLowerCase().split("?")[0];
    return formattedMethod + "~" + formattedUrl;
  }

  $delegate.get = function () {
    var cancelerKey, canceler, method;
    var args = [].slice.call(arguments);
    var url = args[0];
    var config = args[1] || {};
    if (config.timeout == null) {
      method = "GET";
      cancelerKey = getCancelerKey(method, url);
      canceler = $q.defer();
      cancelerMap[cancelerKey] = canceler;
      config.timeout = canceler.promise;
      args[1] = config;
    }
    return getFn.apply(null, args);
  };

  $delegate.abort = function (request) {
    console.log("aborting");
    var cancelerKey, canceler;
    cancelerKey = getCancelerKey(request.method, request.url);
    canceler = cancelerMap[cancelerKey];

    if (canceler != null) {
      console.log("aborting", cancelerKey);

      if (request.timeout != null && typeof request.timeout !== "number") {

        canceler.resolve();
        delete cancelerMap[cancelerKey];
      }
    }
  };

  return $delegate;
}]);
  }]);
rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
  angular.forEach($http.pendingRequests, function (request) {
        $http.abort(request);
    });
  });

出于某种原因,config.timeout对我不起作用。我采用了这种方法:

让cancelRequest=$q.defer; 让cancelPromise=cancelRequest.promise; 让httpPromise=$http.get。。。; $q.race{cancelPromise,httpPromise} .Then函数结果{ ...
}; 这是一个处理多个请求的版本,还检查回调中的取消状态以抑制错误块中的错误。打字

控制器级别:

    requests = new Map<string, ng.IDeferred<{}>>();
在我的http get中:

    getSomething(): void {
        let url = '/api/someaction';
        this.cancel(url); // cancel if this url is in progress

        var req = this.$q.defer();
        this.requests.set(url, req);
        let config: ng.IRequestShortcutConfig = {
            params: { id: someId}
            , timeout: req.promise   // <--- promise to trigger cancellation
        };

        this.$http.post(url, this.getPayload(), config).then(
            promiseValue => this.updateEditor(promiseValue.data as IEditor),
            reason => {
                // if legitimate exception, show error in UI
                if (!this.isCancelled(req)) {
                    this.showError(url, reason)
                }
            },
        ).finally(() => { });
    }
辅助方法

    cancel(url: string) {
        this.requests.forEach((req,key) => {
            if (key == url)
                req.resolve('cancelled');
        });
        this.requests.delete(url);
    }

    isCancelled(req: ng.IDeferred<{}>) {
        var p = req.promise as any; // as any because typings are missing $$state
        return p.$$state && p.$$state.value == 'cancelled';
    }
现在看看网络标签,我发现它工作得很好。我调用了4次该方法,只有最后一次通过


您可以使用将中止函数添加到承诺中的装饰器将自定义函数添加到$http服务中

下面是一些工作代码:

app.config(function($provide) {
    $provide.decorator('$http', function $logDecorator($delegate, $q) {
        $delegate.with_abort = function(options) {
            let abort_defer = $q.defer();
            let new_options = angular.copy(options);
            new_options.timeout = abort_defer.promise;
            let do_throw_error = false;

            let http_promise = $delegate(new_options).then(
                response => response, 
                error => {
                    if(do_throw_error) return $q.reject(error);
                    return $q(() => null); // prevent promise chain propagation
                });

            let real_then = http_promise.then;
            let then_function = function () { 
                return mod_promise(real_then.apply(this, arguments)); 
            };

            function mod_promise(promise) {
                promise.then = then_function;
                promise.abort = (do_throw_error_param = false) => {
                    do_throw_error = do_throw_error_param;
                    abort_defer.resolve();
                };
                return promise;
            }

            return mod_promise(http_promise);
        }

        return $delegate;
    });
});
这段代码使用angularjs的decorator功能向$http服务添加with_abort函数

with_abort使用$http超时选项,允许您中止http请求

将修改返回的承诺以包含中止函数。它也有代码来确保即使你做出了承诺,中止也能正常工作

以下是您将如何使用它的示例:

// your original code
$http({ method: 'GET', url: '/names' }).then(names => {
    do_something(names));
});

// new code with ability to abort
var promise = $http.with_abort({ method: 'GET', url: '/names' }).then(
    function(names) {
        do_something(names));
    });

promise.abort(); // if you want to abort
默认情况下,当您调用abort时,请求将被取消,并且所有承诺处理程序都不会运行

如果希望调用错误处理程序,请将true传递给aborttrue

在错误处理程序中,可以通过检查xhrStatus属性来检查错误是否是由于中止引起的。下面是一个例子:

var promise = $http.with_abort({ method: 'GET', url: '/names' }).then(
    function(names) {
        do_something(names));
    }, 
    function(error) {
        if (er.xhrStatus === "abort") return;
    });

那个PR被拒绝了,OP在这里提交了一个更新的,那个也被关闭了。它的一个版本。仍在试图找出使用最终版本的语法。希望PRs附带使用示例!:“用法”中的angular documentation(角度文档)页面描述了超时设置,还提到了接受承诺的对象。如果我需要超时和通过承诺手动取消,我该怎么办?@RamanChodźka你可以通过承诺同时做这两件事;您可以设置超时,以在一段时间后取消承诺,可以使用JavaScript的本机setTimeout函数或Angular的$timeout service.canceler.resolve取消未来的请求。这是一个更好的解决方案:本·纳德尔(Ben Nadel)的另一个更完整解决方案的好例子:实际上不起作用。你能提供一个工作示例吗?这对我来说很有用-非常简单,我添加了另一个来命名调用,这样我可以选择调用并只取消一些调用。UI路由器配置为什么需要知道request.timeout是否存在?我正在制作一个应用程序,同时触发一些http请求,我需要手动中止它们。我尝试了你的代码,但它只中止了最后一个请求。那以前发生在你身上吗?任何帮助都将不胜感激。此处的代码维护对延迟对象的引用的查找,以便稍后可以检索它们,因为延迟对象需要执行中止。阳具
查找最重要的是键:值对。该值是延迟对象。键是基于请求方法/url生成的字符串。我猜您正在中止对同一方法/url的多个请求。因此,所有关键点都是相同的,它们在地图中相互覆盖。您需要调整密钥生成逻辑,以便即使url/方法相同,也会生成唯一的密钥生成逻辑。。。这不是代码中的错误,代码处理中止多个请求。。。但这段代码根本不打算处理使用同一http方法中止对同一url的多个请求。。。但是如果你调整一下逻辑,你应该可以很容易地让它工作。非常感谢!我对同一个url发出了多个请求,但参数不同,在你提到这一点后,我改变了这一行,它就像一个符咒一样工作!下面的答案实际上都没有取消请求本身。一旦HTTP请求离开浏览器,就无法取消它。下面所有的答案都在某种程度上对听众产生了影响。HTTP请求仍会命中服务器,仍在处理中,服务器仍会发送响应,这只是客户端是否仍在等待该响应的一个例子。$HTTP request cancel on route change code for promise.abort@Liam我的问题没有在服务器上取消。这将非常特定于您的服务器技术/实现。我担心放弃回调你看到你的语法错误了吗{cancelPromise,httpPromise}?这是ES6语法,你可以试试{c:cancelPromise,h:httpPromise}我知道了,object shortinitializerreq.resolve'cancelled';不适用于我,我使用的是1.7.2版本。即使我想取消一个呼叫,如果它再次调用,并且第一个呼叫仍然处于挂起状态。请帮忙。我总是希望通过取消相同url的所有挂起api来提供新调用的调用数据