Join the Stack Overflow Community
Stack Overflow is a community of 6.4 million programmers, just like you, helping each other.
Join them; it only takes a minute:
Sign up

Given a Ajax request in AngularJS

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

what is the most effective way to cancel that request if another request is launched (same backend, different parameters for instance).

share|improve this question
up vote 223 down vote accepted

This feature was added to the 1.1.5 release via a timeout parameter:

var canceler = $q.defer();
$http.get('/someUrl', {timeout: canceler.promise}).success(successCallback);
// later...
canceler.resolve();  // Aborts the $http request if it isn't finished.
share|improve this answer
8  
what should I do in case I need both a timeout and manual cancelling via promise? – Raman Chodźka Jul 26 '13 at 9:56
11  
@RamanChodźka You can do both with a promise; you can set a timeout to cancel the promise after some amount of time, either with JavaScript's native setTimeout function or Angular's $timeout service. – Quinn Strahl Sep 17 '13 at 18:09
6  
canceler.resolve() will cancel future requests. This is a better solution: odetocode.com/blogs/scott/archive/2014/04/24/… – Toolkit Jul 27 '14 at 7:33
5  
another good example of a more complete solution from Ben Nadel: bennadel.com/blog/… – Willshaw Media Jul 29 '14 at 12:34
1  
Doesn't really work. Could you provide a working sample? – Edward Olamisan Mar 31 '15 at 13:59

Cancelation of requests issued with $http is not supported with the current version of AngularJS. There is a pull request opened to add this capability but this PR wasn't reviewed yet so it is not clear if its going to make it into AngularJS core.

share|improve this answer
    
that PR was rejected, OP submitted updated one here github.com/angular/angular.js/pull/1836 – digger69 Feb 5 '13 at 22:47
    
And that was closed as well. – frapontillo Apr 26 '13 at 9:58
    
A version of it landed as this. Still trying to figure out the syntax to use the final version. Wish the PRs came with usage samples! :) – SimplGy Aug 13 '14 at 23:11
    
The angular documentation page docs.angularjs.org/api/ng/service/$http in the 'Usage' describes a timeout setting, and also mentions what objects (a Promise) are accepted. – Igor Lino Sep 22 at 14:37

Cancelling Angular $http Ajax with the timeout property doesn't work in Angular 1.3.15. For those that cannot wait for this to be fixed I'm sharing a jQuery Ajax solution wrapped in Angular.

The solution involves two services:

  • HttpService (a wrapper around the jQuery Ajax function);
  • PendingRequestsService (tracks the pending/open Ajax requests)

Here goes the PendingRequestsService service:

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

The HttpService service:

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

Later in your service when you are loading data you would use the HttpService instead of $http:

(function (angular) {

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

        this.getResources = function (params) {

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

        };
    }]);

})(window.angular);

Later in your code you would like to load the data:

(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);
share|improve this answer

If you want to cancel pending requests on stateChangeStart with ui-router, you can use something like this:

// in service

                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;

// in UIrouter config

$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();
          }
       });
    });
share|improve this answer
    
This worked for me - very simple and i added another one to name the call so i can select the call and only cancel some of the calls – Simon Pertersen Nov 16 '15 at 12:57

This enhances the accepted answer by decorating the $http service with an abort method as follows ...

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

WHAT IS THIS CODE DOING?

To cancel a request a "promise" timeout must be set. If no timeout is set on the HTTP request then the code adds a "promise" timeout. (If a timeout is set already then nothing is changed).

However, to resolve the promise we need a handle on the "deferred". We thus use a map so we can retrieve the "deferred" later. When we call the abort method, the "deferred" is retrieved from the map and then we call the resolve method to cancel the http request.

Hope this helps someone.

LIMITATIONS

Currently this only works for $http.get but you can add code for $http.post and so on

HOW TO USE ...

You can then use it, for example, on state change, as follows ...

rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
  angular.forEach($http.pendingRequests, function (request) {
        $http.abort(request);
    });
  });
share|improve this answer
    
I'm making an app that fires some http requests at the same time and I need to manualy abort them all. I've tried your code but it only aborts the last request. Did that happened to you before? Any help would be appreciated. – Miguel Trabajo Mar 31 at 5:20
1  
the code here maintains a lookup of references to the defer objects so that they can be retrieved later since the defer object is required to do an abort. the important thing with the lookup is the key:value pair. The value is the defer object. The key is a string generated based on the request method / url. I am guessing that you are aborting multiple requests to the same method / url. Because of this all the keys are identical and they overwrite one another in the map. You need to tweak the key generation logic so that a unique one is generated even if the url / method are the same. – danday74 Mar 31 at 10:10
1  
continued from above ... this is not a bug in the code, the code handles aborting multiple requests ... but the code was simply never meant to deal with aborting multiple requests to the same url using the same http method ... but if you tweak the logic you should be able to get it working fairly easily. – danday74 Mar 31 at 10:11
1  
Thank you very much! I was making multiple requests to the same url but with different parameters, and after you said about that I changed that line and it worked like a charm! – Miguel Trabajo Mar 31 at 13:00

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.