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’.
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
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
successCB
mas a response
é undefined
. error.status
será -1
exatamente como se a solicitação expirasse.