Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Feature: Cancel $http request #1159

Closed
coli opened this issue Jul 18, 2012 · 73 comments
Closed

Feature: Cancel $http request #1159

coli opened this issue Jul 18, 2012 · 73 comments

Comments

@coli
Copy link

coli commented Jul 18, 2012

Use case is we started a new $http request, and therefore needs to cancel 1 or more previous $http requests.

Eg:

var previousPromises = [];
previousPromises.push($http().then(function(){}));
...

Then:
//previousPromises[0].cancel()
or
//previousPromises[0].reject()

Reference
https://groups.google.com/forum/?fromgroups#!searchin/angular/cancel$20http/angular/aT-MowZoPJg/kPEr5Ja0GEwJ

@coli
Copy link
Author

coli commented Jul 18, 2012

A slightly related feature is ability to cancel a promise, or if the promise has a reference to the defer, then we can do promise.defered.reject();

@markwaddle
Copy link

i need this feature as well.

i have an interface where a user can quickly trigger many actions that in turn trigger $http requests. in the case that a request is triggered and one is currently waiting for a response, i would like to be able to cancel it before requesting the new one.

@koistya
Copy link

koistya commented Sep 30, 2012

Me too, need that feature.

@markwaddle
Copy link

An alternative to enabling request cancellation would be to allow the client to optionally pass in a key for the request and $http would ensure that only one request per key is pending at any point in time. The latest request with a specific key would always trump prior requests with that key. $http would need to cancel any pending requests with the same key before sending the new request. If the key was falsy it would send the request without consideration for pending requests. In other words, any number of requests with falsy keys could be pending simultaneously. The promises for the cancelled requests could be rejected.

This solution would suit my use case very well, and I suspect others' use cases too, and it might also be simpler to implement because you would not need to change the promise interface.

@samunplugged
Copy link

+1 for this feature.

@jbeard4
Copy link

jbeard4 commented Oct 31, 2012

+1 from me as well

@gregwebs
Copy link

+1

2 similar comments
@jinder
Copy link

jinder commented Nov 14, 2012

+1

@marknadig
Copy link
Contributor

+1

@sssilver
Copy link

+1

1 similar comment
@rickkln
Copy link

rickkln commented Dec 2, 2012

+1

@gonzaloruizdevilla
Copy link
Contributor

@dbinit I think you should reject the promise when the abort method is called. The function that will handle the rejection can use the status parameter to find out if it was an abort request or different kind of error and then act consequently.

@nicolacity
Copy link

Need this feature! +1

@amirnissim
Copy link

+1

3 similar comments
@johnoscott
Copy link

+1

@georgiosd
Copy link

+1

@fredrikbonander
Copy link
Contributor

+1

@rickkln
Copy link

rickkln commented Jan 16, 2013

dbinit's solution worked perfectly for me.

@ryankshaw
Copy link

+1, @dbinit this new fix looks really nice!

@ryankshaw
Copy link

another thing that i need is to be able to bind to the progress event of the xhr upload.

for example (in plain javascript):

var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", uploadProgress, false)

I want to be able to do the same thing by doing $http.post...

so maybe rather than just adding an abort method, it would be good to provide something that lets me just get at the original xhr object.

@markwaddle
Copy link

@ryankshaw that sounds like a separate request deserving of its own issue

  • Mark

On Wed, Jan 30, 2013 at 4:07 PM, Ryan Shaw notifications@github.com wrote:

another thing that i need is to be able to bind to the progress event of
the xhr upload.

for example (in plain javascript):

var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", uploadProgress, false)

I want to be able to do the same thing by doing $http.post...

so maybe rather than just adding an abort method, it would be good to
provide something that lets me just get at the original xhr object.


Reply to this email directly or view it on GitHubhttps://github.com//issues/1159#issuecomment-12920190.

@ryankshaw
Copy link

@markwaddle, I just filed it as a separate issue in #1934

@jorlow
Copy link

jorlow commented Feb 21, 2013

+1

2 similar comments
@mping
Copy link

mping commented Feb 28, 2013

+1

@ybogdanov
Copy link

+1

@fxck
Copy link

fxck commented Jul 2, 2013

Any reason this shouldn't work? Because it doesn't fire the request at all..

        el.bind('keyup', function() {

            var canceler = $q.defer();

            $http.post('/api', data, {timeout: canceler.promise}).success(function(returnData) {
            });
            canceler.resolve();
        });

@dbinit
Copy link
Contributor

dbinit commented Jul 2, 2013

You need to wrap your code with scope.$apply. Bind doesn't do it implicitly.

@fxck
Copy link

fxck commented Jul 2, 2013

$q needs it? Becuase $http doesn't.. anyway it' still not firing the request no matter if I put it inside scope.$apply(function(){}); or just put scope.$apply() after it..

@dbinit
Copy link
Contributor

dbinit commented Jul 2, 2013

Hmm, I think the $http.post doesn't fire the request until the next digest cycle. So the canceler.resolve is canceling it before it happens.

Try putting a scope.$apply between the $http.post and canceler.resolve.

@gonzaloruizdevilla
Copy link
Contributor

It seems to me like it is working.
You resolve the canceler promise immediately just after creating the request. You don't give any time to the request.

@knalli
Copy link

knalli commented Jul 2, 2013

Ehm, that's AngularJS. Every external event call must invoke the scope digester.

$http will create the request with the next scope cycle.. the example above have not one.

@fxck
Copy link

fxck commented Jul 2, 2013

@dbinit that works, thanks!

@elemoine
Copy link

elemoine commented Jul 2, 2013

See #2442 for the issue reporting on $http requiring to be called within a $watch. That really sounds like a bug to me, as one should be able to call $http from any context.

@e-oz
Copy link

e-oz commented Jul 18, 2013

great thing. thank you very much!

@markstickley
Copy link

Seems like I'm about 4 months too late but doesn't the approach of hijacking the timeout parameter means that you can't have both a fixed timeout AND the option to manually cancel (due to the request being made obsolete) without rolling your own timeout functionality? That seems rather limited. Do we know if separating this joint functionality into discrete parts is on the roadmap at all?

@coli
Copy link
Author

coli commented Oct 3, 2013

@markstickley It's just a promise you control, it has nothing to do with timeout

@dbinit
Copy link
Contributor

dbinit commented Oct 3, 2013

@markstickley You can just use the promise returned by $timeout and then do $timeout.cancel(promise) if you want to cancel the request early.

@markstickley
Copy link

@dbinit That's a decent workaround, thanks I'll try that :)

@btford
Copy link
Contributor

btford commented Oct 4, 2013

I wrote a service demonstrating how one might encapsulate this functionality here: http://plnkr.co/edit/P8wKns51GVqw5mwS5l5R?p=preview

I thought it might be useful for anyone that comes across this thread in the future.

@derekrprice
Copy link

btford's plunker isn't completely working for me. The cancelQuery = null in $http.post().success() backfires. success() gets called asynchronously for cancelled requests, so with a query launched on keypress, if I type quickly some of the cancelQuery info is lost, meaning only some requests are cancelled. If I just remove the cancelQuery = null line, everything works fine.

@phantomwhale
Copy link

I ended up bringing in a boolean flag, so that I could differentiate between a "normal" error, and a manually cancelled error. The need to do this does seem to highlight this is indeed a "workaround" and perhaps could do with some better direct support at a later stage.

I modified @btford 's plunk to demonstrate this: http://plnkr.co/edit/BW6Zwu?p=preview

basarat added a commit to DefinitelyTyped/DefinitelyTyped that referenced this issue Nov 18, 2013
@fxck
Copy link

fxck commented Nov 19, 2013

has this stopped working for anyone in 1.2.1?

@anton000
Copy link

instead of relying on timeout, ended up wrapping $http in another deferred, then rejected that deferred when a new call was made or when i needed to "cancel". this had the result of still completing the $http call, but rejecting the data returned.

thoughts?

$scope.fetchData = function() {
  if ($scope.async) {
    $scope.async.reject();
  }
  $scope.async = testAsync();
  $scope.async.promise.then(
    function(data){
    console.log('success' + data);
    },
    function(data) {
      console.log('reject' + data);
    }
  );
}

testAsync = function() {
  var deferred = $q.defer();
  $http.get('/api/')
    .success(function(data) {
      deferred.resolve(data);
    })
    .error(function(data) {
      deferred.reject(data);
    });
  return deferred;
};

@snjoetw
Copy link

snjoetw commented Mar 28, 2014

@anton000 your code only rejects the wrapper promise. it doesn't actually cancel original $http promise

@anton000
Copy link

@snjoetw i explained in my post it still completes the original $http promise but rejects the data returned. I think using this matches the concept of promise to resolver communication being unidirectional as @IgorMinar explained in #2452 .

@MaestroJurko
Copy link

@anton000
Copy link

anton000 commented Jul 8, 2014

@mato75 This would be better asked on the blog itself which you got the snippet from, or over on sites like http://www.stackoverflow.com

@BiosSun
Copy link

BiosSun commented Dec 4, 2014

+1

@codeniac
Copy link

codeniac commented Apr 1, 2015

How is going this feature? I need it a lot!!! 👍

@e-oz
Copy link

e-oz commented Apr 1, 2015

@codeniac read here https://docs.angularjs.org/api/ng/service/$http#usage about timeout argument.

@ghost
Copy link

ghost commented Apr 17, 2015

couldn't you use config.timeout = $q.when('canceled') to cancel an $http request before it's actually sent?

This was referenced Mar 13, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet