Angularjs como cancelar a promise de resources ao alternar rotas

Eu estou apenas começando meus pés molhados com Angularjs. Eu tenho um problema que acho que tem algo a ver com promises.

Digamos que eu carregue a rota ‘A’, que faz vários pedidos de ajax através do seu controlador:

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

Então eu tenho rota ‘B’, que faz sua própria solicitação de API através de seu controlador:

 $scope.categories = Category.query(); 

Aqui está o serviço de fábrica atualmente usado pela rota ‘A’:

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

Quando vejo pela primeira vez a rota ‘A’, mas mudo para ‘B’ antes que ‘A’ termine de carregar, a rota ‘B’ fica e aguarda que tudo inicialmente solicitado em ‘A’ termine (na verdade, a solicitação query () é feita , mas não vai resolver até que o de ‘A’, naquele momento, o material dentro. .then() continua a acontecer, mesmo que eu não precise dele como estou agora em outra rota.

Como você pode ver na linha de tempo do meu devtools, a linha verde indica quando mudei para a rota ‘B’. A solicitação para a rota ‘B’ não foi resolvida até que as duas solicitações acima tenham ocorrido (uma solicitação que geralmente é muito rápida). (em que ponto eu sou capaz de usar a visão como um usuário). Então, depois disso, mais promises resolvem da rota ‘A’.

Meus devtools

Procurei por toda parte por uma resposta e só posso encontrar pessoas que desejem “adiar” o carregamento da rota até que as promises sejam resolvidas. Mas no meu caso eu quase quero o oposto. Eu quero matar esses pedidos quando eu mudar.

Aqui está outra pessoa com a mesma pergunta não respondida: Rejeitar promises de resources do Angularjs

Qualquer ajuda é apreciada.

Primeiro de tudo, eu decidi que precisava usar o $http pois não consegui encontrar nenhuma solução que usasse $resource , nem poderia fazê-lo funcionar sozinho.

Então aqui está o que minha fábrica transformou, com base na resposta do @ Sid aqui, usando o guia em 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's 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 }); }); 

Então, no meu controlador (rota ‘A’ do meu problema):

 var allSitesPromise = AllSites.getSites(categoryID); $scope.$on('$destroy',function(){ allSitesPromise.abort(); }); allSitesPromise.then(function(allSites){ // do stuff here with the result } 

Eu queria que a fábrica não fosse tão bagunçada, mas vou pegar o que puder. No entanto, agora há uma questão relacionada separada aqui onde, embora a promise tenha sido cancelada, as próximas ações ainda estão atrasadas. Se você tem uma resposta para isso, você pode postar lá.

Há uma pergunta semelhante com a resposta ” Como cancelar solicitações de recurso $ “.

Apesar de não abordar a questão exatamente, ela fornece todos os ingredientes para cancelar a solicitação de recurso quando a rota é alternada:

     Cancel resource      

Como funciona

  1. $ resource mescla definição de ação, solicita parâmetros e dados para construir um parâmetro de configuração para uma solicitação $ http.
  2. um parâmetro de configuração passado para uma requisição $ http é tratado como uma promise como object, então ele pode conter a function para inicializar a configuração.
  3. a function de ação então pode passar a promise de tempo limite de params para a configuração.

Por favor, olhe para ” Cancelar Angularjs resource request ” para detalhes.

Dê uma olhada neste post

Você poderia fazer o que ele está fazendo e resolver a promise de abortar a solicitação em uma mudança de rota (ou alteração de estado se estiver usando o roteador ui).

Pode não ser a coisa mais fácil de acontecer, mas parece que pode funcionar.

Eu cancelo a promise com $ q.reject (). Eu acho que isso é mais simples:

Em 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 } } })() 

Em 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 }) } } })() 

Verificando os documentos para $resource encontrei um link para esta pequena beleza. https://docs.angularjs.org/api/ng/service/ $ http # usage

timeout – {number | Promise} – tempo limite em milissegundos ou promise que deve anular o pedido quando resolvido.

Eu usei isso com algum sucesso. Vai um pouco algo assim.

 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:  params: args, timeout: canceller }).then(successCB, errorCB); function successCB (response) { return response.data; } function errorCB (error) { return $q.reject(error.data); } } } 

Tenha em mente

  1. Isso pressupõe que você deseja apenas os resultados da última solicitação
  2. As solicitações canceladas ainda chamam o successCB mas a response é undefined .
  3. Ele também pode chamar errorCB, o error.status será -1 exatamente como se a solicitação expirasse.