如何在AngularJS中取消$http请求?
在AngularJS中给出了一个Ajax请求如何在AngularJS中取消$http请求?,angularjs,promise,angular-promise,angular-http,cancellation,Angularjs,Promise,Angular Promise,Angular Http,Cancellation,在AngularJS中给出了一个Ajax请求 $http.get("/backend/").success(callback); 如果启动了另一个请求(例如相同的后端,不同的参数),取消该请求的最有效方法是什么。当前版本的AngularJS不支持取消使用$http发出的请求。有一种方法可以添加此功能,但此PR尚未审核,因此不清楚它是否会进入AngularJS core。此功能是通过超时参数实现的: var canceler = $q.defer(); $http.get('/someUrl',
$http.get("/backend/").success(callback);
如果启动了另一个请求(例如相同的后端,不同的参数),取消该请求的最有效方法是什么。当前版本的AngularJS不支持取消使用
$http
发出的请求。有一种方法可以添加此功能,但此PR尚未审核,因此不清楚它是否会进入AngularJS core。此功能是通过超时参数实现的:
var canceler = $q.defer();
$http.get('/someUrl', {timeout: canceler.promise}).success(successCallback);
// later...
canceler.resolve(); // Aborts the $http request if it isn't finished.
在Angular 1.3.15中,使用timeout属性取消Angular$http Ajax不起作用。 对于那些迫不及待要解决这个问题的人,我将分享一个用Angular包装的jQueryAjax解决方案 该解决方案涉及两项服务:
- HttpService(jQueryAjax函数的包装器)李>
- PendingRequestsService(跟踪挂起/打开的Ajax请求)
(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);
(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服务:
(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);
(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);
如果您想取消stateChangeStart with 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();
}
});
});
这通过如下所示的abort方法装饰$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;
}]);
}]);
rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
angular.forEach($http.pendingRequests, function (request) {
$http.abort(request);
});
});
这段代码在做什么
要取消请求,必须设置“承诺”超时。
如果HTTP请求没有设置超时,那么代码会添加一个“承诺”超时。
(如果已设置超时,则不会更改任何内容)
然而,要解决承诺,我们需要处理“延期”问题。
因此,我们使用一个映射,以便稍后可以检索“延迟的”。
当我们调用abort方法时,将从映射中检索“deferred”,然后调用resolve方法取消http请求
希望这对别人有帮助
局限性
目前,这只适用于$http.get,但您可以为$http.post等添加代码
如何使用
然后,您可以在状态更改时使用它,如下所示
'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;
}]);
}]);
rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
angular.forEach($http.pendingRequests, function (request) {
$http.abort(request);
});
});
出于某种原因,config.timeout对我不起作用。我采用了这种方法:
let cancelRequest=$q.defer();
让cancelPromise=cancelRequest.promise;
让httpPromise=$http.get(…);
$q.race({cancelPromise,httpPromise})
.然后(函数(结果){
...
});代码>这是一个处理多个请求的版本,还检查回调中的取消状态以抑制错误块中的错误。(打字稿)
控制器级别:
requests = new Map<string, ng.IDeferred<{}>>();
requests=newmap();
在我的http get中:
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(() => { });
}
getSomething():void{
让url='/api/someaction';
this.cancel(url);//如果此url正在运行,则取消
var req=此$q.延迟();
this.requests.set(url,req);
let config:ng.IRequestShortcutConfig={
参数:{id:someId}
,超时:req.promise//this.updateEditor(promiseValue.data作为IEditor),
原因=>{
//如果异常合法,则在UI中显示错误
如果(!此.isCancelled(请求)){
此.bathror(url、原因)
}
},
).最后(()=>{});
}
辅助方法
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';
}
cancel(url:string){
this.requests.forEach((请求,键)=>{
if(key==url)
请求解决(“取消”);
});
this.requests.delete(url);
}
已取消(要求:已取消){
var p=req.promise as any;//as any,因为键入缺少$$state
返回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的decorator功能向$http
服务添加with_abort()
函数
with_abort()
使用允许您中止http请求的$http
超时选项
返回的承诺被修改为包含一个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传递给中止(true)
在错误处理程序中,您可以通过检查xhrStatus
属性来检查“错误”是否是由于“中止”造成的。以下是一个示例:
var promise = $http.with_abort({ method: 'GET', url: '/names' }).then(
function(names) {
do_something(names));
},
function(error) {
if (er.xhrStatus === "abort") return;
});
该PR被拒绝,OP在此处提交了更新的PR,该PR也被关闭。该PR的一个版本。仍在尝试找出使用最终版本的语法。希望PRs附带使用示例!:)在“使用”中的angular documentation页面描述了超时设置,还提到了哪些对象(承诺)被接受。如果我需要超时和通过承诺手动取消,我该怎么办?@RamanChodźka您可以通过承诺同时执行;您可以设置超时以在一段时间后取消承诺,可以使用JavaScript的本机setTimeout
函数或Angular的$timeout
服务。canceler.resolve()将取消未来的请求。这是一个更好的解决方案:Ben Nadel提供的更完整解决方案的另一个很好的示例:实际上不起作用。您能提供一个工作示例吗?这对我来说很有效-非常简单,我添加了另一个示例来命名呼叫,这样我可以选择呼叫并仅取消部分呼叫为什么UI路由器配置需要k现在,如果存在请求超时,我正在制作一个应用程序