Как отменить запрос $ http в AngularJS?
Учитывая запрос Ajax в AngularJS
$http.get("/backend/").success(callback);
что является наиболее эффективным способом отменить этот запрос, если запускается другой запрос (тот же бэкэнд, например, разные параметры).
- Как предотвратить обнаружение меток привязки?
- Как включить один частичный в другой, не создавая новую область?
- Мне нужно два экземпляра услуги AngularJS $ http или что?
- петля foreach в angularjs
- Может ли controller AngularJS наследовать от другого controllerа в том же модуле?
- Как можно решить npm UNEPT PEER DEPENDENCY?
- Ждите всех обещаний решить
- Отображать blob (.pdf) в угловом приложении
- Хороший способ динамически открывать / закрывать popover (или всплывающую подсказку), используя угловой, основанный на выражении?
- Как использовать ng-class в select с ng-options
- Жасмин: обратный вызов Async не вызывался в течение таймаута, указанного jasmine.DEFAULT_TIMEOUT_INTERVAL
- AngularJS: правильный способ привязки к свойствам услуги
- Как добавить некоторые небольшие служебные функции в приложение AngularJS?
Эта функция была добавлена к версии 1.1.5 через параметр таймаута:
var canceler = $q.defer(); $http.get('/someUrl', {timeout: canceler.promise}).success(successCallback); // later... canceler.resolve(); // Aborts the $http request if it isn't finished.
Отмена углового $ http Ajax с тайм-аутом не работает в Angular 1.3.15. Для тех, кто не может дождаться, когда это будет исправлено, я использую решение jQuery Ajax, завернутое в Angular.
Решение включает в себя две службы:
- HttpService (shell вокруг функции jQuery Ajax);
- PendingRequestsService (отслеживает ожидающие / открытые запросы Ajax)
Здесь работает служба PendingRequestsService:
(function (angular) { 'use strict'; var app = angular.module('app'); app.service('PendingRequestsService', ["$log", function ($log) { var $this = this; var pending = []; $this.add = function (request) { pending.push(request); }; $this.remove = function (request) { pending = _.filter(pending, function (p) { return p.url !== request; }); }; $this.cancelAll = function () { angular.forEach(pending, function (p) { p.xhr.abort(); p.deferred.reject(); }); pending.length = 0; }; }]);})(window.angular);
Служба HttpService:
(function (angular) { 'use strict'; var app = angular.module('app'); app.service('HttpService', ['$http', '$q', "$log", 'PendingRequestsService', function ($http, $q, $log, pendingRequests) { this.post = function (url, params) { var deferred = $q.defer(); var xhr = $.ASI.callMethod({ url: url, data: params, error: function() { $log.log("ajax error"); } }); pendingRequests.add({ url: url, xhr: xhr, deferred: deferred }); xhr.done(function (data, textStatus, jqXhr) { deferred.resolve(data); }) .fail(function (jqXhr, textStatus, errorThrown) { deferred.reject(errorThrown); }).always(function (dataOrjqXhr, textStatus, jqXhrErrorThrown) { //Once a request has failed or succeeded, remove it from the pending list pendingRequests.remove(url); }); return deferred.promise; } }]); })(window.angular);
Позже, когда вы загружаете данные, вы должны использовать HttpService вместо $ http:
(function (angular) { angular.module('app').service('dataService', ["HttpService", function (httpService) { this.getResources = function (params) { return httpService.post('/serverMethod', { param: params }); }; }]); })(window.angular);
Позже в вашем коде вы хотели бы загрузить данные:
(function (angular) { var app = angular.module('app'); app.controller('YourController', ["DataService", "PendingRequestsService", function (httpService, pendingRequestsService) { dataService .getResources(params) .then(function (data) { // do stuff }); ... // later that day cancel requests pendingRequestsService.cancelAll(); }]); })(window.angular);
Отмена запросов, выданных с помощью $http
, не поддерживается текущей версией AngularJS. Для добавления этой возможности существует запрос на перенос, но этот PR еще не был рассмотрен, поэтому неясно, собирается ли он сделать его в ядре AngularJS.
Если вы хотите отменить ожидающие запросы на stateChangeStart с помощью ui-router, вы можете использовать что-то вроде этого:
// в сервисе
var deferred = $q.defer(); var scope = this; $http.get(URL, {timeout : deferred.promise, cancel : deferred}).success(function(data){ //do something deferred.resolve(dataUsage); }).error(function(){ deferred.reject(); }); return deferred.promise;
// в конфигурации UIrouter
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) { //To cancel pending request when change state angular.forEach($http.pendingRequests, function(request) { if (request.cancel && request.timeout) { request.cancel.resolve(); } }); });
По какой-то причине config.timeout не работает для меня. Я использовал этот подход:
let cancelRequest = $q.defer(); let cancelPromise = cancelRequest.promise; let httpPromise = $http.get(...); $q.race({ cancelPromise, httpPromise }) .then(function (result) { ... });
Это улучшает принятый ответ, украсив службу $ http методом прерывания следующим образом …
'use strict'; angular.module('admin') .config(["$provide", function ($provide) { $provide.decorator('$http', ["$delegate", "$q", function ($delegate, $q) { var getFn = $delegate.get; var cancelerMap = {}; function getCancelerKey(method, url) { var formattedMethod = method.toLowerCase(); var formattedUrl = encodeURI(url).toLowerCase().split("?")[0]; return formattedMethod + "~" + formattedUrl; } $delegate.get = function () { var cancelerKey, canceler, method; var args = [].slice.call(arguments); var url = args[0]; var config = args[1] || {}; if (config.timeout == null) { method = "GET"; cancelerKey = getCancelerKey(method, url); canceler = $q.defer(); cancelerMap[cancelerKey] = canceler; config.timeout = canceler.promise; args[1] = config; } return getFn.apply(null, args); }; $delegate.abort = function (request) { console.log("aborting"); var cancelerKey, canceler; cancelerKey = getCancelerKey(request.method, request.url); canceler = cancelerMap[cancelerKey]; if (canceler != null) { console.log("aborting", cancelerKey); if (request.timeout != null && typeof request.timeout !== "number") { canceler.resolve(); delete cancelerMap[cancelerKey]; } } }; return $delegate; }]); }]);
ЧТО ЭТОТ КОД?
Чтобы отменить запрос, должен быть установлен тайм-аут «promise». Если в HTTP-запросе не установлен тайм-аут, тогда код добавляет тайм-аут «обещания». (Если тайм-аут уже установлен, ничего не изменяется).
Однако для решения обещания нам нужен дескриптор «отложенного». Таким образом, мы используем карту, поэтому мы можем позже получить «отложенную». Когда мы вызываем метод прерывания, «отложенный» извлекается с карты, а затем мы вызываем метод разрешения, чтобы отменить HTTP-запрос.
Надеюсь, это поможет кому-то.
ОГРАНИЧЕНИЯ
В настоящее время это работает только для $ http.get, но вы можете добавить код для $ http.post и так далее
КАК ИСПОЛЬЗОВАТЬ …
Затем вы можете использовать его, например, при изменении состояния, следующим образом …
rootScope.$on('$stateChangeStart', function (event, toState, toParams) { angular.forEach($http.pendingRequests, function (request) { $http.abort(request); }); });
вот версия, которая обрабатывает несколько запросов, а также проверяет отмененный статус в обратном вызове для подавления ошибок в блоке ошибок. (в машинописном тексте)
уровень controllerа:
requests = new Map>();
в моем http получить:
getSomething(): void { let url = '/api/someaction'; this.cancel(url); // cancel if this url is in progress var req = this.$q.defer(); this.requests.set(url, req); let config: ng.IRequestShortcutConfig = { params: { id: someId} , timeout: req.promise // <--- promise to trigger cancellation }; this.$http.post(url, this.getPayload(), config).then( promiseValue => this.updateEditor(promiseValue.data as IEditor), reason => { // if legitimate exception, show error in UI if (!this.isCancelled(req)) { this.showError(url, reason) } }, ).finally(() => { }); }
вспомогательные методы
cancel(url: string) { this.requests.forEach((req,key) => { if (key == url) req.resolve('cancelled'); }); this.requests.delete(url); } isCancelled(req: ng.IDeferred<{}>) { var p = req.promise as any; // as any because typings are missing $$state return p.$$state && p.$$state.value == 'cancelled'; }
теперь, смотря на вкладку сети, я вижу, что она работает блательно. я назвал метод 4 раза, и только последний прошел.
Вы можете добавить пользовательскую функцию в службу $http
с помощью «декоратора», который добавит функцию abort()
к вашим обещаниям.
Вот какой рабочий код:
app.config(function($provide) { $provide.decorator('$http', function $logDecorator($delegate, $q) { $delegate.with_abort = function(options) { let abort_defer = $q.defer(); let new_options = angular.copy(options); new_options.timeout = abort_defer.promise; let do_throw_error = false; let http_promise = $delegate(new_options).then( response => response, error => { if(do_throw_error) return $q.reject(error); return $q(() => null); // prevent promise chain propagation }); let real_then = http_promise.then; let then_function = function () { return mod_promise(real_then.apply(this, arguments)); }; function mod_promise(promise) { promise.then = then_function; promise.abort = (do_throw_error_param = false) => { do_throw_error = do_throw_error_param; abort_defer.resolve(); }; return promise; } return mod_promise(http_promise); } return $delegate; }); });
Этот код использует функции декоратора angularjs для добавления функции with_abort()
в службу $http
.
with_abort()
использует опцию $http
timeout, которая позволяет отменить HTTP-запрос.
Возвращенное promise изменено, чтобы включить функцию abort()
. Он также имеет код, чтобы убедиться, что abort()
работает, даже если вы связываете обещания.
Вот пример того, как вы его используете:
// your original code $http({ method: 'GET', url: '/names' }).then(names => { do_something(names)); }); // new code with ability to abort var promise = $http.with_abort({ method: 'GET', url: '/names' }).then( function(names) { do_something(names)); }); promise.abort(); // if you want to abort
По умолчанию, когда вы вызываете abort()
запрос отменяется, и ни один из обработчиков обещаний не запускается.
Если вы хотите, чтобы ваши обработчики ошибок вызывались, передайте true, чтобы abort(true)
.
В обработчике ошибок вы можете проверить, была ли «ошибка» вызвана «отменой» путем проверки свойства xhrStatus
. Вот пример:
var promise = $http.with_abort({ method: 'GET', url: '/names' }).then( function(names) { do_something(names)); }, function(error) { if (er.xhrStatus === "abort") return; });