Подтвердить что ты не робот

Angularjs как отменить ресурс при переключении маршрутов

Я просто мочу ноги с помощью Angularjs. У меня проблема, которая, как я думаю, имеет отношение к promises.

Скажем, я загружаю маршрут "А" , который через несколько контроллеров выполняет несколько аякс-запросов:

allSites = AllSites.query({ id:categoryID });

allSites.$promise.then(function(allSites){
    //add stuff to the scope and does other things 
    //(including making another ajax request)
});

Затем у меня есть маршрут "B" , который делает его собственным запросом API через контроллер:

$scope.categories = Category.query();

Здесь factory служба, используемая в настоящее время по маршруту "A":

.factory('AllSites',function($resource){
    return $resource('api/categorySites/:id');
});

Когда я сначала просматриваю маршрут "А" , а затем переключись на "В" до того, как "А" закончит загрузку, маршрут "В" сидит и ждет все, что первоначально запрошено в "А" , чтобы закончить (на самом деле запрос() запрос будет выполнен, но он не будет разрешаться до тех пор, пока тот, который находится от "А" , в этот момент продолжает существовать внутри .then(), хотя мне он не нужен, поскольку я сейчас на другом маршруте.

Как вы можете видеть на моей временной шкале devtools, зеленая линия указывает, когда я переключился на маршрут "B" . Запрос на маршрут "B" не разрешался до тех пор, пока не будут выполнены указанные выше два запроса (запрос, который обычно выполняется очень быстро). (в этот момент я могу использовать представление в качестве пользователя). Затем после этого больше promises разрешается с маршрута "A".

My devtools

Я искал всюду для ответа и могу найти людей, которые хотят "отложить" загрузку маршрута до тех пор, пока не будут решены promises. Но в моем случае я почти хочу наоборот. Я хочу убить эти запросы при переключении.

Здесь кто-то другой с тем же, неотвеченным вопросом: Отклонить ресурс Angularjs promises

Любая помощь приветствуется.

4b9b3361

Ответ 1

Прежде всего, я решил, что мне нужно использовать $http, так как я не смог найти решение, которое использовало $resource, и я не мог заставить его работать самостоятельно.

Итак, вот что мой factory превратился в, основываясь на @Sid, здесь, используя руководство по http://www.bennadel.com/blog/2616-aborting-ajax-requests-using-http-and-angularjs.htm

.factory('AllSites',function($http,$q){

    function getSites(categoryID) {

        // The timeout property of the http request takes a deferred value
        // that will abort the underying AJAX request if / when the deferred
        // value is resolved.
        var deferredAbort  = $q.defer();

        // Initiate the AJAX request.
        var request = $http({
            method: 'get',
            url: 'api/categorySites/'+categoryID,
            timeout: deferredAbort.promise
        });

        // Rather than returning the http-promise object, we want to pipe it
        // through another promise so that we can "unwrap" the response
        // without letting the http-transport mechansim leak out of the
        // service layer.
        var promise = request.then(
            function( response ) {
                return( response.data );
            },
            function() {
                return( $q.reject( 'Something went wrong' ) );
            }
        );

        // Now that we have the promise that we're going to return to the
        // calling context, let augment it with the abort method. Since
        // the $http service uses a deferred value for the timeout, then
        // all we have to do here is resolve the value and AngularJS will
        // abort the underlying AJAX request.
        promise.abort = function() {
            deferredAbort.resolve();
        };

        // Since we're creating functions and passing them out of scope,
        // we're creating object references that may be hard to garbage
        // collect. As such, we can perform some clean-up once we know
        // that the requests has finished.
        promise.finally(
            function() {
                promise.abort = angular.noop;
                deferredAbort = request = promise = null;
            }
        );

        return( promise );
    }

    // Return the public API.
    return({
        getSites: getSites
    });

});

Затем в моем контроллере (маршрут "A" из моей проблемы):

var allSitesPromise = AllSites.getSites(categoryID);

$scope.$on('$destroy',function(){
    allSitesPromise.abort();
});

allSitesPromise.then(function(allSites){
    // do stuff here with the result
}

Я бы хотел, чтобы factory не был настолько запутанным, но я возьму то, что смогу получить. Однако теперь есть отдельный, связанный с этим вопрос здесь, где, хотя обещание было отменено, следующие действия по-прежнему задерживаются. Если у вас есть ответ на этот вопрос, вы можете отправить его там.

Ответ 2

Есть аналогичный вопрос с ответом Как отменить $resource request".

Пока он не решает вопрос точно, он дает всем ингредиентам отменять запрос ресурсов при переключении маршрута:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>Cancel resource</title>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-resource.js"></script>
  <script>
angular.module("app", ["ngResource"]).
factory(
  "services",
  ["$resource", function($resource)
  {
    function resolveAction(resolve)
    {
      if (this.params)
      {
        this.timeout = this.params.timeout;
        this.params.timeout = null;
      }

      this.then = null;
      resolve(this);
    }

    return $resource(
      "http://md5.jsontest.com/",
      {},
      {
        MD5:
        {
          method: "GET",
          params: { text: null },
          then: resolveAction
        },
      });
  }]).
controller(
  "Test",
  ["services", "$q", "$timeout", function(services, $q, $timeout)
  {
    this.value = "Sample text";
    this.requestTimeout = 100;

    this.call = function()
    {
      var self = this;

      self.result = services.MD5(
      {
        text: self.value,
        timeout: $q(function(resolve)
        {
          $timeout(resolve, self.requestTimeout);
        })
      });
    }
  }]);
  </script>
</head>
<body ng-app="app" ng-controller="Test as test">
  <label>Text: <input type="text" ng-model="test.value" /></label><br/>
  <label>Timeout: <input type="text" ng-model="test.requestTimeout" /></label><br/>
  <input type="button" value="call" ng-click="test.call()"/>
  <div ng-bind="test.result.md5"></div>
</body>
</html>

Как это работает

  • $resource объединяет определение действия, запрашивает параметры и данные для создания параметра конфигурации для запроса $http.
  • параметр конфигурации, переданный в запрос $http, рассматривается как объект, похожий на обещание, поэтому он может содержать функцию для инициализации конфигурации.
  • действие, то функция может передать время ожидания из параметров в конфигурацию.

Подробнее см. Отменить запрос ресурса Angularjs".

Ответ 3

Взгляните на этот пост

Вы можете делать то, что он делает, и разрешить обещание прервать запрос при изменении маршрута (или изменить состояние при использовании маршрутизатора ui).

Возможно, это не самая простая вещь, но похоже, что она может работать.

Ответ 4

Я отменяю обещание с помощью $q.reject(). Я думаю, что этот способ проще:

В SitesServices.js:

;(() => {
  app.services('SitesServices', sitesServices)
  sitesServices.$inject = ['$http', '$q']
  function sitesServices($http, $q) {

    var sitesPromise = $q.defer()
    this.getSites = () => {
      var url = 'api/sites'
      sitesPromise.reject()
      sitesPromise = $q.defer()
      $http.get(url)
        .success(sitesPromise.resolve)
        .error(sitesPromise.reject)

      return sitesPromise.promise
    }
  }
})()

В SitesController.js:

;(() => {
  app.controller('SitesController', sitesControler)
  sitesControler.$inject = ['$scope', 'SitesServices']
  function sitesControler($scope, SitesServices) {
    $scope.sites = []

    $scope.getSites = () => {
      SitesServices.getSites().then(sites => {
        $scope.sites = sites
      })
    }
  }
})()

Ответ 5

Проверка документов для $resource Я нашел ссылку на эту маленькую красоту. https://docs.angularjs.org/api/ng/service/ $http # use

timeout - {number | Promise} - тайм-аут в миллисекундах или обещание должен прервать запрос при его разрешении.

Я использовал его с некоторым успехом. Это немного похоже на это.

export default function MyService($q, $http) {
  "ngInject";
  var service = {
    getStuff: getStuff,
  };

  let _cancelGetStuff = angular.noop;

  return service;

  function getStuff(args) {
    _cancelGetStuff(); // cancel any previous request that might be ongoing.

    let canceller = $q( resolve => { _cancelGetStuff = resolve; });

    return $http({
      method: "GET",
      url: <MYURL>
      params: args,
      timeout: canceller
    }).then(successCB, errorCB);

    function successCB (response) {
      return response.data;
    }

    function errorCB (error) {
      return $q.reject(error.data);
    }
  }
}

Имейте в виду

  • Предполагается, что вы хотите получать результаты только от последнего запроса
  • Отмененные запросы по-прежнему вызывают successCB, но response - undefined.
  • Он также может вызывать errorCB, error.status будет -1 точно так же, как если бы запрос был истекло.