6

On route change, I need to abort ALL pending requests from previous route so that I don't run into problems of responses from previous route messing up data on my current route (it happens sometimes when responses from previous route take long time to finish).

I have thought about using http interceptor for this:

$httpProvider.interceptors.push(function($q) {
    return {
     'request': function(config) {

      },

      'response': function(response) {

      }
    };
  });

In the request function, I could modify the config.timeout with a promise as suggested here and store all the deferred objects in a global cache so that I could cancel all of them.

The problem with this approach is that it may override config.timeout set in other places in the code.

I think another solution could be to cancel all ajax requests at XMLHttpRequest level, but I don't know how to do it.

Any suggestions? Thanks.

Community
  • 1
  • 1
Khanh TO
  • 48,509
  • 13
  • 99
  • 115
  • If the `config.timeout` is already set in other place, you could still combine it with the promise for this purpose together. – runTarm Aug 14 '14 at 05:15
  • Even if there was a built in way to cancel all pending `XMLHttpRequest`s wouldn't it cause trouble by i.e. not loading html templates. Furthermore it's very common to have an fire&forget action which you don't want to cancel even though you've moved to another screen... – miensol Aug 14 '14 at 05:18
  • @runTarm: how to do it? Anyway, I think that `timeout` is another concept separated from `abort`. It's a trick when we take advantage of `timeout` to abort a request (we should avoid tricks when coding). – Khanh TO Aug 14 '14 at 05:51
  • @miensol: it would not cause trouble not loading html because I've moved to another route. I'm working on an existing code base, I cannot just fire&forget, this is causing problems in the app. – Khanh TO Aug 14 '14 at 05:54
  • I don't think the `timeout` is a trick or a dirty hack. It is the only API that `$http` service exposed for aborting ajax request, at least as of now. – runTarm Aug 14 '14 at 06:04
  • 1
    @runTarm: I think I agree with the second answer from the link in the question: http://stackoverflow.com/questions/13928057/how-to-cancel-an-http-request-in-angularjs – Khanh TO Aug 14 '14 at 06:10

1 Answers1

7

As you say, timeout is the only API we have of use right now to cancel a running $http request. I think you're right on the money with an interceptor coupled with a cancel promise.

What you could do is attach the full deferred object on the $http request, and cancel all pendingRequests in your route change handler.

Something like this could (perhaps*) work?

angular.module('module').config(function ($httpProvider) {
  $httpProvider.interceptors.push(function ($q) {
    return {
      request: function (config) {
        if (!config.timeout) {
          config.cancel  = $q.defer();
          config.timeout = config.cancel.promise;            
        }

        return config;
      }
    }
  });
});

angular.module('module').run(function ($rootScope, $http) {
  $rootScope.$on('$stateChangeStart', function () {
    $http.pendingRequests.forEach(function (pendingReq) {
      if (pendingReq.cancel) {
        pendingReq.cancel.resolve('Cancel!');
      }
    });
  });
});

*: I say perhaps, because I had success with this approach, but it's seldom you find a silver bullet to something like this.


edit

If you need to bypass the error handler of the cancelled promise, hook into responseError property and do manual labour there.

angular.module('module').config(function ($httpProvider) {
  $httpProvider.interceptors.push(function ($q) {
    return {
      responseError: function (response) {
        if (response.config.timeout.$$state.value === 'Cancel!') {
          // debugger; 
          return $q.when('bypassed');
        }
      }
    }
  });
});

I'm starting to think that there is no generic/'cool' solution to reach the end result you desire. Treading some odd ground here : )


edit2:

Testing the above myself now. Returning $q.when('something') in the responseError will effectively bypass the error callback of the cancelled $http request. Let me know if it works out for you.

  • thanks for the answer, but as i said: `The problem with this approach is that it may override config.timeout set in other places in the code.` – Khanh TO Jul 09 '15 at 12:58
  • Hence the `if (!config.timeout)` condition. –  Jul 09 '15 at 13:03
  • another problem I forgot to mention. When we cancel using timeout promise, the error callback is called which is not desirable. How can we solve this? I asked the question because I thought there may be other solutions without using timeout promise. – Khanh TO Jul 09 '15 at 13:07
  • edited my answer to touch on where you could possibly bypass the error/success handler of the cancelled promise. –  Jul 09 '15 at 13:22
  • Thanks for the solution sir !!, works well, didn't find any problems / bugs so far :P, any other solutions ? – que1326 Jan 21 '17 at 13:28
  • Thanks for the solution. But is the following error intentionally made to break the callback flow? `Cannot create property 'resource' on string 'bypassed'` – Shashwat Black Apr 07 '17 at 06:21