Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cancelling a request with a $http interceptor?

Tags:

angularjs

I'm trying to figure out if it is possible to use a $http interceptor to cancel a request before it even happens.

There is a button that triggers a request but if the user double-clicks it I do not want the same request to get triggered twice.

Now, I realize that there's several ways to solve this, and we do already have a working solution where we wrap $http in a service that keeps track of requests that are currently pending and simply ignores new requests with the same method, url and data.

Basically this is the behaviour I am trying to do with an interceptor:

factory('httpService', ['$http', function($http) {

    var pendingCalls = {};

    var createKey = function(url, data, method) {
        return method + url + JSON.stringify(data);
    };

    var send = function(url, data, method) {
        var key = createKey(url, data, method);
        if (pendingCalls[key]) {
            return pendingCalls[key];
        }
        var promise = $http({
            method: method,
            url: url,
            data: data
        });
        pendingCalls[key] = promise;
        promise.finally(function() {
            delete pendingCalls[key];
        });
        return promise;
    };

    return {
        post: function(url, data) {
            return send(url, data, 'POST');
        }
    }

}])

When I look at the API for $http interceptors it does not seem to be a way to achieve this. I have access to the config object but that's about it.

Am I attempting to step outside the boundaries of what interceptors can be used for here or is there a way to do it?

like image 666
ivarni Avatar asked Feb 28 '14 09:02

ivarni


2 Answers

according to $http documentation, you can return your own config from request interceptor.

try something like this:

config(function($httpProvider) {
    var cache = {};

    $httpProvider.interceptors.push(function() {
        return {
            response : function(config) {
                var key = createKey(config);
                var cached = cache[key];
                return cached ? cached : cached[key];    
            }
        }
    });
}
like image 111
ilj Avatar answered Oct 17 '22 15:10

ilj


Very old question, but I'll give a shot to handle this situation.

If I understood correctly, you are trying to:

1 - Start a request and register something to refer back to it;

2 - If another request takes place, to the same endpoint, you want to retrieve that first reference and drop the request in it.

This might be handled by a request timeout in the $http config object. On the interceptor, you can verify it there's one registered on the current request, if not, you can setup one, keep a reference to it and handle if afterwards:

function DropoutInterceptor($injector) {
    var $q = $q || $injector.get('$q');
    var dropouts = {};

    return {
        'request': function(config) {
            // I'm using the request's URL here to make
            // this reference, but this can be bad for
            // some situations.
            if (dropouts.hasOwnProperty(config.url)) {
                // Drop the request
                dropouts[config.url].resolve();
            }

            dropouts[config.url] = $q.defer();

            // If the request already have one timeout
            // defined, keep it, othwerwise, set up ours.
            config.timeout = config.timeout || dropouts[config.url];

            return config;
        },
        'requestError': function(reason) {
            delete dropouts[reason.config.url];

            return $q.reject(reason);
        },
        'response': function(response) {
            delete dropouts[response.config.url];

            return response;
        },
        'responseError': function(reason) {
            delete dropouts[reason.config.url];

            return $q.reject(reason);
        }
    };
}
like image 21
Mateus Leon Avatar answered Oct 17 '22 16:10

Mateus Leon