Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determining if AJAX is cancelled or got an error in AngularJs interceptor

Tags:

angularjs

I have a "cancellable" angularJs $http call like this:

var defer = $q.defer()
$http.post("http://example.com/someUrl", {some: "data"}, {timeout: defer.promise})

And I cancel that ajax request using defer.resolve() because some logic requires it.


Some where else in my code, I have an iterceptor like this:

angular.module("services.interceptor", arguments).config(function($httpProvider) {
     $httpProvider.interceptors.push(function($q) {
        return {
          responseError: function(rejection) {
            if(rejection.status == 0) {
              alert("check your connection");
            }
           return $q.reject(rejection);
        }
      };
    });
  });
});

Problem:

If there is an Internet connection problem, ajax fails with status 0 and interceptor catches it. If ajax is cancelled by the timeout promise, than status is also 0 and interceptor catches it.

I can't find out if it is cancelled or got error in responseError handler.

My naive approach is to check if timeout is defined like this:

responseError: function(rejection) {
  if(rejection.status == 0 && !rejection.config.timeout) {
    alert("check your connection");
  }
  return $q.reject(rejection);
}

It only guarantees that, there is a timeout condition on that request, not it failed because of it. But it is better than nothing.

Are there a really working way of determining if ajax is failed or cancelled?

I'm using AngularJs 1.1.5

like image 268
Umut Benzer Avatar asked Aug 15 '13 10:08

Umut Benzer


1 Answers

I ended up doing this using a flag, which is checked within the error handler.

I started off following the advice at the end of this AngularJS Github issue: https://github.com/angular/angular.js/issues/1159#issuecomment-25735438

This led me to build a service which used a promise to manually "timeout" the http call - as you have done below - by copying the linked plunk from that issue: http://plnkr.co/edit/P8wKns51GVqw5mwS5l5R?p=preview

But this wasn't quite complete, as this didn't address the issue you raised; in the error handling, how to determine if the call was a genuine server outage / timeout, or simply a manually triggered timeout. In the end, I had to resort to storing it in another variable (similar to the timeout promise), which can be seen in this plunk: http://plnkr.co/edit/BW6Zwu

The main method of this code looks as follows

return function (url) {
    var cancelQuery = null;
    var queryCancelled = false;

    return function runQuery(query) {
      if (cancelQuery) {
        queryCancelled = true;
        cancelQuery.resolve();
      }
      cancelQuery = $q.defer();
      return $http.
        get(url, { params: { query: query }, timeout: cancelQuery.promise }).
        then(function (response) {
          cancelQuery = null;
          return response.data;
        }, function (error) {
          if(queryCancelled) {
            console.log("Request cancelled");
            queryCancelled = false;
          } else {
            console.log("Actual Error !");
          }
        });
    };
  };

Not too elegant, but it seems to work and avoids any nasty race conditions from what I have observed.

like image 131
Phantomwhale Avatar answered Oct 25 '22 10:10

Phantomwhale