Angularjs cómo cancelar la promesa de recursos al cambiar de ruta

Me estoy mojando los pies con Angularjs. Tengo un problema que creo que tiene algo que ver con las promesas.

Digamos que cargo la ruta ‘A’ que hace varias solicitudes ajax a través de su 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) }); 

Luego tengo la ruta ‘B’ que hace su propia solicitud de API a través de su controlador:

 $scope.categories = Category.query(); 

Aquí está el servicio de fábrica utilizado actualmente por la ruta ‘A’:

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

Cuando veo la ruta ‘A’ por primera vez y luego cambío a ‘B’ antes de que ‘A’ termine de cargarse, la ruta ‘B’ se queda y espera a que todo inicialmente solicitado en ‘A’ termine (en realidad, la solicitud de consulta () , pero no se resolverá hasta que lo haga la ‘A’, en ese punto, las cosas dentro de .then() continúan sucediendo, aunque no lo necesito ya que ahora estoy en otra ruta.

Como puede ver en mi línea de tiempo de devtools, la línea verde indica cuando cambié a la ruta ‘B’. La solicitud de la ruta ‘B’ no se resolvió hasta que las dos solicitudes anteriores lo hicieron (una solicitud que suele ser muy rápida). (en ese punto puedo usar la vista como usuario). Luego, después de eso, más promesas se resuelven desde la ruta ‘A’.

Mis devtools

Busqué una respuesta en todas partes y solo puedo encontrar personas que quieran “aplazar” la carga de la ruta hasta que se resuelvan las promesas. Pero en mi caso, casi quiero lo opuesto. Quiero matar esas solicitudes cuando cambio.

Aquí hay otra persona con la misma pregunta sin respuesta: Rechazar las promesas de recursos de Angularjs

Cualquier ayuda es apreciada.

Antes que nada, decidí que necesitaba usar $http ya que no pude encontrar ninguna solución que utilizara $resource , ni podría hacerlo funcionar solo.

Así que aquí es en lo que se convirtió mi fábrica, basado en la respuesta de @ Sid aquí, usando la guía en 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 }); }); 

Luego, en mi controlador (ruta ‘A’ de mi problema):

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

Ojalá la fábrica no fuera tan desordenada, pero tomaré lo que pueda. Sin embargo, ahora hay un problema separado relacionado aquí donde, aunque se canceló la promesa, las siguientes acciones todavía se retrasan. Si tiene una respuesta para eso, puede publicarla allí.

Hay una pregunta similar con la respuesta ” Cómo cancelar $ solicitudes de recursos “.

Si bien no aborda la pregunta exactamente, proporciona todos los ingredientes para cancelar la solicitud de recursos cuando se cambia la ruta:

     Cancel resource      

Cómo funciona

  1. $ resource combina la definición de acción, solicita parámetros y datos para construir un parámetro de configuración para una solicitud $ http.
  2. un parámetro de configuración pasado a una solicitud $ http se trata como un objeto promisorio, por lo que puede contener la función para inicializar la configuración.
  3. La función action de action puede pasar la promesa de timeout desde params a la configuración.

Consulte ” Cancelar solicitud de recurso Angularjs ” para obtener más información.

Echa un vistazo a esta publicación

Podría hacer lo que está haciendo y resolver la promesa de abortar la solicitud en un cambio de ruta (o cambio de estado si usa el enrutador ui).

Puede que no sea lo más fácil de hacer, pero parece que puede funcionar.

Cancelo la promesa con $ q.reject (). Creo que de esta manera es más simple:

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

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

Comprobando los documentos de $resource encontré un enlace a esta pequeña belleza. https://docs.angularjs.org/api/ng/service/ $ http # usage

timeout – {number | Promise} – agote el tiempo en milisegundos, o prometa que debe abortar la solicitud cuando se resuelva.

Lo he usado con algo de éxito. Es algo como esto.

 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); } } } 

Tenga en cuenta

  1. Esto supone que solo quieres los resultados de la última solicitud
  2. Las solicitudes canceladas todavía llaman a successCB pero la response undefined está undefined .
  3. También puede llamar a errorCB, error.status será -1 al igual que si se error.status el tiempo de solicitud.