我试图找出如何使用$资源的timeout属性动态取消挂起的请求。理想情况下,我想只能取消请求与某些属性(基于发送的params),但似乎这可能是不可能的。在此期间,我只是试图取消所有待处理的请求,然后重置超时承诺允许新的请求。
问题似乎是$ resource配置只允许一个单一的静态promise的超时值。如果我正在进行单独的$ http调用,我可以做到这一点是有意义的,因为我可以只传递新的承诺超时,但是如何工作的一个$资源?我在这里设置了一个示例:http://plnkr.co/edit/PP2tqDYXh1NAOU3yqCwP?p=preview
这里是我的控制器代码:
app.controller('MainCtrl',function($scope,$timeout,$q,$resource) { $scope.canceller = $q.defer(); $scope.pending = 0; $scope.actions = []; var API = $resource( 'index.html',{},{ get: { method: 'GET',timeout: $scope.canceller.promise } } ) $scope.fetchData = function() { if ($scope.pending) { $scope.abortPending(); } $scope.pending = 1; $scope.actions.push('request'); API.get({},function() { $scope.actions.push('completed'); $scope.pending = 0; },function() { $scope.actions.push('aborted'); }); } $scope.abortPending = function() { $scope.canceller.resolve(); $scope.canceller = $q.defer(); } });
现在,当有一个挂起的请求时,取消器工作,但我似乎不能重置它 – 一旦一个请求被中止,所有未来的请求也将被中止。
我相信我错过了一些东西,因为能够取消挂起的请求似乎是大多数Web应用程序(至少我已经构建)的一个非常关键的功能。
谢谢
回答
stefchri为我工作,但我不得不进行一些修改,以便:
>启用资源ajax调用将被取消多次,而无需重新创建资源
>使资源向后兼容 – 这意味着不需要更改除资源工厂之外的任何应用程序(控制器)代码
>使代码与JSLint兼容
这是完整的服务工厂实现(你只需要放正确的模块名称):
'use strict'; /** * ResourceFactory creates cancelable resources. * Work based on: http://stackoverflow.com/a/25448672/1677187 * which is based on: https://developer.rackspace.com/blog/cancelling-ajax-requests-in-angularjs-applications/ */ /* global array */ angular.module('module_name').factory('ResourceFactory',['$q','$resource',function($q,$resource) { function abortablePromiseWrap(promise,deferred,outstanding) { promise.then(function() { deferred.resolve.apply(deferred,arguments); }); promise.catch(function() { deferred.reject.apply(deferred,arguments); }); /** * Remove from the outstanding array * on abort when deferred is rejected * and/or promise is resolved/rejected. */ deferred.promise.finally(function() { array.remove(outstanding,deferred); }); outstanding.push(deferred); } function createResource(url,options,actions) { var resource; var outstanding = []; actions = actions || {}; Object.keys(actions).forEach(function(action) { var canceller = $q.defer(); actions[action].timeout = canceller.promise; actions[action].Canceller = canceller; }); resource = $resource(url,actions); Object.keys(actions).forEach(function(action) { var method = resource[action]; resource[action] = function() { var deferred = $q.defer(),promise = method.apply(null,arguments).$promise; abortablePromiseWrap(promise,outstanding); return { $promise: deferred.promise,abort: function() { deferred.reject('Aborted'); },cancel: function() { actions[action].Canceller.resolve('Call cancelled'); // Recreate canceler so that request can be executed again var canceller = $q.defer(); actions[action].timeout = canceller.promise; actions[action].Canceller = canceller; } }; }; }); /** * Abort all the outstanding requests on * this $resource. Calls promise.reject() on outstanding []. */ resource.abortAll = function() { for (var i = 0; i < outstanding.length; i++) { outstanding[i].reject('Aborted all'); } outstanding = []; }; return resource; } return { createResource: function (url,actions) { return createResource(url,actions); } }; } ]);
用法与stefchri示例中的相同。服务工厂:
'use strict'; angular.module('module_name').factory('YourResourceServiceName',['ResourceFactory',function(ResourceFactory) { return ResourceFactory.createResource('some/api/path/:id',{ id: '@id' },{ create: { method: 'POST' },update: { method: 'PUT' } }); }]);
控制器中的用法(向后兼容):
var result = YourResourceServiceName.create(data); result.$promise.then(function success(data,responseHeaders) { // Successfully obtained data },function error(httpResponse) { if (httpResponse.status === 0 && httpResponse.data === null) { // Request has been canceled } else { // Server error } }); result.cancel(); // Cancels XHR request
替代方法:
var result = YourResourceServiceName.create(data); result.$promise.then(function success(data,responseHeaders) { // Successfully obtained data }).catch(function (httpResponse) { if (httpResponse.status === 0 && httpResponse.data === null) { // Request has been canceled } else { // Server error } }); result.cancel(); // Cancels XHR request
进一步改进:
>我不喜欢检查请求是否已取消。更好的方法是在请求被取消时附加属性httpResponse.isCanceled,并且中止类似。