Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit d641901

Browse files
mracgkalpak
authored andcommitted
feat($resource): add support for timeout in cancellable actions
Old behavior: actions can be either cancellable or have a numeric timeout. When having both defined, cancellable was ignored. With this commit: it's possible for actions to have both cancellable:true and numeric timeout defined. Example usage: ```js var Post = $resource('/posts/:id', {id: '@id'}, { get: { method: 'GET', cancellable: true, timeout: 10000 } }); var currentPost = Post.get({id: 1}); ... // the next request can cancel the previous one currentPost.$cancelRequest(); currentPost = Post.get({id: 2}); // any of those requests will also timeout, if the response // doesn't come within 10 seconds ``` Closes #13824
1 parent eae0a11 commit d641901

File tree

2 files changed

+52
-21
lines changed

2 files changed

+52
-21
lines changed

src/ngResource/resource.js

+22-17
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ function shallowClearAndCopy(src, dst) {
6565
* @requires $http
6666
* @requires ng.$log
6767
* @requires $q
68+
* @requires $timeout
6869
*
6970
* @description
7071
* A factory which creates a resource object that lets you interact with
@@ -160,7 +161,6 @@ function shallowClearAndCopy(src, dst) {
160161
* will be cancelled (if not already completed) by calling `$cancelRequest()` on the call's
161162
* return value. Calling `$cancelRequest()` for a non-cancellable or an already
162163
* completed/cancelled request will have no effect.<br />
163-
* **Note:** If a timeout is specified in millisecondes, `cancellable` is ignored.
164164
* - **`withCredentials`** - `{boolean}` - whether to set the `withCredentials` flag on the
165165
* XHR object. See
166166
* [requests with credentials](https://developer.mozilla.org/en/http_access_control#section_5)
@@ -416,7 +416,7 @@ angular.module('ngResource', ['ng']).
416416
}
417417
};
418418

419-
this.$get = ['$http', '$log', '$q', function($http, $log, $q) {
419+
this.$get = ['$http', '$log', '$q', '$timeout', function($http, $log, $q, $timeout) {
420420

421421
var noop = angular.noop,
422422
forEach = angular.forEach,
@@ -575,20 +575,19 @@ angular.module('ngResource', ['ng']).
575575

576576
forEach(actions, function(action, name) {
577577
var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
578-
var cancellable = false;
579-
580-
if (!angular.isNumber(action.timeout)) {
581-
if (action.timeout) {
582-
$log.debug('ngResource:\n' +
583-
' Only numeric values are allowed as `timeout`.\n' +
584-
' Promises are not supported in $resource, because the same value would ' +
585-
'be used for multiple requests. If you are looking for a way to cancel ' +
586-
'requests, you should use the `cancellable` option.');
587-
delete action.timeout;
588-
}
589-
cancellable = angular.isDefined(action.cancellable) ? action.cancellable :
590-
(options && angular.isDefined(options.cancellable)) ? options.cancellable :
591-
provider.defaults.cancellable;
578+
var numericTimeout = action.timeout;
579+
var cancellable = angular.isDefined(action.cancellable) ? action.cancellable :
580+
(options && angular.isDefined(options.cancellable)) ? options.cancellable :
581+
provider.defaults.cancellable;
582+
583+
if (numericTimeout && !angular.isNumber(numericTimeout)) {
584+
$log.debug('ngResource:\n' +
585+
' Only numeric values are allowed as `timeout`.\n' +
586+
' Promises are not supported in $resource, because the same value would ' +
587+
'be used for multiple requests. If you are looking for a way to cancel ' +
588+
'requests, you should use the `cancellable` option.');
589+
delete action.timeout;
590+
numericTimeout = null;
592591
}
593592

594593
Resource[name] = function(a1, a2, a3, a4) {
@@ -639,6 +638,7 @@ angular.module('ngResource', ['ng']).
639638
var responseErrorInterceptor = action.interceptor && action.interceptor.responseError ||
640639
undefined;
641640
var timeoutDeferred;
641+
var numericTimeoutPromise;
642642

643643
forEach(action, function(value, key) {
644644
switch (key) {
@@ -656,6 +656,10 @@ angular.module('ngResource', ['ng']).
656656
if (!isInstanceCall && cancellable) {
657657
timeoutDeferred = $q.defer();
658658
httpConfig.timeout = timeoutDeferred.promise;
659+
660+
if (numericTimeout) {
661+
numericTimeoutPromise = $timeout(timeoutDeferred.resolve, numericTimeout);
662+
}
659663
}
660664

661665
if (hasBody) httpConfig.data = data;
@@ -706,7 +710,8 @@ angular.module('ngResource', ['ng']).
706710
value.$resolved = true;
707711
if (!isInstanceCall && cancellable) {
708712
value.$cancelRequest = angular.noop;
709-
timeoutDeferred = httpConfig.timeout = null;
713+
$timeout.cancel(numericTimeoutPromise);
714+
timeoutDeferred = numericTimeoutPromise = httpConfig.timeout = null;
710715
}
711716
});
712717

test/ngResource/resourceSpec.js

+30-4
Original file line numberDiff line numberDiff line change
@@ -1372,6 +1372,7 @@ describe('cancelling requests', function() {
13721372
var httpSpy;
13731373
var $httpBackend;
13741374
var $resource;
1375+
var $timeout;
13751376

13761377
beforeEach(module('ngResource', function($provide) {
13771378
$provide.decorator('$http', function($delegate) {
@@ -1380,9 +1381,10 @@ describe('cancelling requests', function() {
13801381
});
13811382
}));
13821383

1383-
beforeEach(inject(function(_$httpBackend_, _$resource_) {
1384+
beforeEach(inject(function(_$httpBackend_, _$resource_, _$timeout_) {
13841385
$httpBackend = _$httpBackend_;
13851386
$resource = _$resource_;
1387+
$timeout = _$timeout_;
13861388
}));
13871389

13881390
it('should accept numeric timeouts in actions and pass them to $http', function() {
@@ -1531,7 +1533,9 @@ describe('cancelling requests', function() {
15311533
expect(creditCard3.$cancelRequest).toBeUndefined();
15321534
});
15331535

1534-
it('should not make the request cancellable if there is a timeout', function() {
1536+
it('should accept numeric timeouts in cancellable actions and cancel the request when timeout occurs', function() {
1537+
$httpBackend.whenGET('/CreditCard').respond({});
1538+
15351539
var CreditCard = $resource('/CreditCard', {}, {
15361540
get: {
15371541
method: 'GET',
@@ -1540,9 +1544,13 @@ describe('cancelling requests', function() {
15401544
}
15411545
});
15421546

1543-
var creditCard = CreditCard.get();
1547+
CreditCard.get();
1548+
$timeout.flush();
1549+
expect($httpBackend.flush).toThrow(new Error('No pending request to flush !'));
1550+
1551+
CreditCard.get();
1552+
expect($httpBackend.flush).not.toThrow();
15441553

1545-
expect(creditCard.$cancelRequest).toBeUndefined();
15461554
});
15471555

15481556
it('should cancel the request (if cancellable), when calling `$cancelRequest`', function() {
@@ -1562,6 +1570,24 @@ describe('cancelling requests', function() {
15621570
expect($httpBackend.flush).not.toThrow();
15631571
});
15641572

1573+
it('should cancel the request, when calling `$cancelRequest` in cancellable actions with timeout defined', function() {
1574+
$httpBackend.whenGET('/CreditCard').respond({});
1575+
1576+
var CreditCard = $resource('/CreditCard', {}, {
1577+
get: {
1578+
method: 'GET',
1579+
timeout: 10000,
1580+
cancellable: true
1581+
}
1582+
});
1583+
1584+
CreditCard.get().$cancelRequest();
1585+
expect($httpBackend.flush).toThrow(new Error('No pending request to flush !'));
1586+
1587+
CreditCard.get();
1588+
expect($httpBackend.flush).not.toThrow();
1589+
});
1590+
15651591
it('should reset `$cancelRequest` after the response arrives', function() {
15661592
$httpBackend.whenGET('/CreditCard').respond({});
15671593

0 commit comments

Comments
 (0)