How to cancel an $http request in AngularJS?

125,576

Solution 1

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.

Solution 2

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

Solution 3

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.

Solution 4

For some reason config.timeout doesn't work for me. I used this approach:

let cancelRequest = $q.defer();
let cancelPromise = cancelRequest.promise;

let httpPromise = $http.get(...);

$q.race({ cancelPromise, httpPromise })
    .then(function (result) {
...
});

And cancelRequest.resolve() to cancel. Actually it doesn't not cancel a request but you don't get unnecessary response at least.

Hope this helps.

Solution 5

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:
125,576

Related videos on Youtube

mpm
Author by

mpm

Angularjs, Nodejs ,Android and Symfony

Updated on May 19, 2020

Comments

  • mpm
    mpm about 4 years

    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).

    • Liam
      Liam over 7 years
      None of the answers below actually cancel the request itself. There is no way to cancel a HTTP request once it leaves the browser. All the below answers simply abondon the listener in some way. The HTTP request still hits the server, is still processed and the server will still send a response, it's just a case of wether the client is still lisening for that response or not.
    • Luis Perez
      Luis Perez about 6 years
      code for promise.abort() stackoverflow.com/a/50415480/984780
    • Sonic Soul
      Sonic Soul almost 5 years
      @Liam my question was not cancelling on the server. that would be very specific to what your server technology/implementation is. i was concerned with abandoning the callback
  • Mark Nadig
    Mark Nadig over 11 years
    that PR was rejected, OP submitted updated one here github.com/angular/angular.js/pull/1836
  • frapontillo
    frapontillo about 11 years
    And that was closed as well.
  • Raman Chodźka
    Raman Chodźka almost 11 years
    what should I do in case I need both a timeout and manual cancelling via promise?
  • Quinn Strahl
    Quinn Strahl almost 11 years
    @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.
  • mirichan
    mirichan over 10 years
    Thanks so much! My previous solution was just using jQuery for Ajax requests, and I really wanted to avoid that.
  • Toolkit
    Toolkit almost 10 years
    canceler.resolve() will cancel future requests. This is a better solution: odetocode.com/blogs/scott/archive/2014/04/24/…
  • Pete
    Pete almost 10 years
    another good example of a more complete solution from Ben Nadel: bennadel.com/blog/…
  • SimplGy
    SimplGy almost 10 years
    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! :)
  • Edward Olamisan
    Edward Olamisan about 9 years
    Doesn't really work. Could you provide a working sample?
  • Simon Dragsbæk
    Simon Dragsbæk over 8 years
    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
  • Jitendra Khatri
    Jitendra Khatri over 8 years
    Tried to implement this to cancel all pending requests on route change. All Pending requests are getting canceled But, still success callback (provided in controller) executing and console displaying errors (response is undefined) when we change route while there are some requests in pending status. (Those requests got canceled).
  • Miguel Trabajo
    Miguel Trabajo about 8 years
    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.
  • danday74
    danday74 about 8 years
    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
    danday74 about 8 years
    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.
  • Miguel Trabajo
    Miguel Trabajo about 8 years
    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!
  • Zorgatone
    Zorgatone about 8 years
    probably using canceller.reject() instead @JitendraKhatri?
  • Sahasrangshu Guha
    Sahasrangshu Guha almost 8 years
    As i'm seeing in my network panel of dev tool of chrome, the download is still in progress even the resolve is called.
  • Igor Lino
    Igor Lino almost 8 years
    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.
  • Mephiztopheles
    Mephiztopheles over 7 years
    Did you see your SyntaxError { cancelPromise, httpPromise }?
  • Aliaksandr Hmyrak
    Aliaksandr Hmyrak over 7 years
    this is ES6 syntax, you can try { c: cancelPromise, h: httpPromise }
  • Mephiztopheles
    Mephiztopheles over 7 years
    I see, object shortinitializer
  • gaurav5430
    gaurav5430 about 7 years
    @Toolkit it will cancel future requests as it is already resolved, but if we do var canceler = $q.defer(); before every request, it would work.
  • trysis
    trysis almost 7 years
    Why does the UI Router config need to know if request.timeout is present?
  • Luis Perez
    Luis Perez about 6 years
    For working code that lets you call promise.abort() checkout stackoverflow.com/a/50415480/984780
  • John Lee
    John Lee over 5 years
    Confirming this method still works in late 2018. The bug in 1.3.x that some of the other answers/comment allude to must have been fixed. I started going on tangents while debugging the code thinking it might be angular that might be broken. In case anyone is thinking about going on same tangents, look more carefully at your code. This simple code works!
  • Sudarshan Kalebere
    Sudarshan Kalebere almost 5 years
    req.resolve('cancelled'); is not working for me, i am using 1.7.2 version. Even i want to cancel a call if it is called again and first call is still in pending state. please help. i always want to provide newly called call data by canceling all pending api's of same url
  • Sudarshan Kalebere
    Sudarshan Kalebere almost 5 years
    anyone can please let me know which methods works for 1.7.2, to cancel particular pending api request?
  • Huon Imberger
    Huon Imberger over 4 years
    @SudarshanKalebere I can confirm the above works for Angluar 1.7.8. The http request promise will reject and the error object will contain xhrStatus: 'abort' or something similar.