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
Любая помощь приветствуется.
Ответы
Ответ 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
точно так же, как если бы запрос был истекло.