AngularJS abort all pending $http requests on route change

前端 未结 4 1970
日久生厌
日久生厌 2020-11-28 03:09

Please go through the code first

app.js

var app = angular.module(\'Nimbus\', [\'ngRoute\']);

route.js

app.config(fu         


        
相关标签:
4条回答
  • 2020-11-28 03:48

    I put together some conceptual code for this. It might need tweaking to fit your needs. There's a pendingRequests service that has an API for adding, getting and cancelling requests, a httpService that wraps $http and makes sure all requests are tracked.

    By leveraging the $http config object (docs) we can get a way to cancel a pending request.

    I've made a plnkr, but you're going to need quick fingers to see requests getting cancelled since the test-site I found typically responds within half a second, but you will see in the devtools network tab that requests do get cancelled. In your case, you would obviously trigger the cancelAll() call on the appropriate events from $routeProvider.

    The controller is just there to demonstrate the concept.

    DEMO

    angular.module('app', [])
    // This service keeps track of pending requests
    .service('pendingRequests', function() {
      var pending = [];
      this.get = function() {
        return 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.canceller.resolve();
        });
        pending.length = 0;
      };
    })
    // This service wraps $http to make sure pending requests are tracked 
    .service('httpService', ['$http', '$q', 'pendingRequests', function($http, $q, pendingRequests) {
      this.get = function(url) {
        var canceller = $q.defer();
        pendingRequests.add({
          url: url,
          canceller: canceller
        });
        //Request gets cancelled if the timeout-promise is resolved
        var requestPromise = $http.get(url, { timeout: canceller.promise });
        //Once a request has failed or succeeded, remove it from the pending list
        requestPromise.finally(function() {
          pendingRequests.remove(url);
        });
        return requestPromise;
      }
    }])
    // The controller just helps generate requests and keep a visual track of pending ones
    .controller('AppCtrl', ['$scope', 'httpService', 'pendingRequests', function($scope, httpService, pendingRequests) {
      $scope.requests = [];
      $scope.$watch(function() {
        return pendingRequests.get();
      }, function(pending) {
        $scope.requests = pending;
      })
    
      var counter = 1;
      $scope.addRequests = function() {
        for (var i = 0, l = 9; i < l; i++) {
          httpService.get('https://public.opencpu.org/ocpu/library/?foo=' + counter++);  
        }
      };
      $scope.cancelAll = function() {
        pendingRequests.cancelAll();
      }
    }]);
    
    0 讨论(0)
  • 2020-11-28 04:11

    I think this is the best solution to abort requests. It's using an interceptor and $routeChangeSuccess event. http://blog.xebia.com/cancelling-http-requests-for-fun-and-profit/

    0 讨论(0)
  • 2020-11-28 04:12

    Please notice that im new with Angular so this may not be optimal. Another solution could be: on the $http request adding the "timeout" argument, Docs I did it this way:

    In a factory where I call all my Rest services, have this logic.

    module.factory('myactory', ['$http', '$q', function ($http, $q) {
        var canceler = $q.defer();
    
        var urlBase = '/api/blabla';
        var factory = {};
    
        factory.CANCEL_REQUESTS = function () {
            canceler.resolve();
            this.ENABLE_REQUESTS();
        };
        factory.ENABLE_REQUESTS = function () {
            canceler = $q.defer();
        };
        factory.myMethod = function () {
            return $http.get(urlBase, {timeout: canceler.promise});
        };
        factory.myOtherMethod= function () {
            return $http.post(urlBase, {a:a, b:b}, {timeout: canceler.promise});
        };
        return factory;
    }]);
    

    and on the angular app configuration I have:

    return angular.module('app', ['ngRoute', 'ngSanitize', 'app.controllers', 'app.factories',
        'app.filters', 'app.directives', 'ui.bootstrap', 'ngGeolocation', 'ui.select' ])
    .run(['$location', '$rootScope', 'myFactory', function($location, $rootScope, myFactory) {
        $rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
            myFactory.CANCEL_REQUESTS();
            $rootScope.title = current.$$route.title;
        });
    }]);
    

    This way it catches all the "route" changes and stops all the request configured with that "timer" so you can select what is critical for you.

    I hope it helps to someone. Regards

    0 讨论(0)
  • 2020-11-28 04:15

    You can use $http.pendingRequests to do that.

    First, when you make request, do this:

    var cancel = $q.defer();
    var request = {
        method: method,
        url: requestUrl,
        data: data,
        timeout: cancel.promise, // cancel promise, standard thing in $http request
        cancel: cancel // this is where we do our magic
    };
    
    $http(request).then(.....);
    

    Now, we cancel all our pending requests in $routeChangeStart

    $rootScope.$on('$routeChangeStart', function (event, next, current) {
    
        $http.pendingRequests.forEach(function(request) {
            if (request.cancel) {
                request.cancel.resolve();
            }
        });
    });
    

    This way you can also 'protect' certain request from being cancelled by simply not providing 'cancel' field in request.

    0 讨论(0)
提交回复
热议问题