Skip to content

Commit eaf0cd7

Browse files
committed
feat($http,$resource): statusText resolved in timeout promise
Timeout-promise resolved-value will be passed as response error statusText ```js $http({ method: 'GET', url: '...', timeout: $timeout(function() { return "something"; }, 500) }).catch(function(err) { console.log(err.statusText); // something }); ``` This feature will also help distinguish cancelled $resource requests from timed-out $resource requests. Before both cases resulted with the same error object: ```{status: -1, statusText: '', ...}``` and were not giving much options to handle them in different way. Now cancelled requests will come with statusText='cancelled' and timed-out requests with statusText='timeout' in error object.
1 parent b8b5b88 commit eaf0cd7

File tree

6 files changed

+86
-11
lines changed

6 files changed

+86
-11
lines changed

src/ng/httpBackend.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,12 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
105105
statusText);
106106
};
107107

108+
var timeoutStatusText;
109+
108110
var requestError = function() {
109111
// The response is always empty
110112
// See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
111-
completeRequest(callback, -1, null, null, '');
113+
completeRequest(callback, -1, null, null, timeoutStatusText || '');
112114
};
113115

114116
xhr.onerror = requestError;
@@ -145,7 +147,8 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
145147
}
146148

147149

148-
function timeoutRequest() {
150+
function timeoutRequest(statusText) {
151+
timeoutStatusText = statusText;
149152
jsonpDone && jsonpDone();
150153
xhr && xhr.abort();
151154
}
@@ -155,7 +158,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
155158
if (isDefined(timeoutId)) {
156159
$browserDefer.cancel(timeoutId);
157160
}
158-
jsonpDone = xhr = null;
161+
jsonpDone = xhr = timeoutStatusText = null;
159162

160163
callback(status, response, headersString, statusText);
161164
$browser.$$completeOutstandingRequest(noop);

src/ngMock/angular-mocks.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,11 +1346,15 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
13461346
copy(response[3] || ''));
13471347
}
13481348

1349-
function handleTimeout() {
1349+
function handleTimeout(statusText) {
13501350
for (var i = 0, ii = responses.length; i < ii; i++) {
13511351
if (responses[i] === handleResponse) {
13521352
responses.splice(i, 1);
1353-
callback(-1, undefined, '');
1353+
if (statusText) {
1354+
callback(-1, undefined, '', statusText);
1355+
} else {
1356+
callback(-1, undefined, '');
1357+
}
13541358
break;
13551359
}
13561360
}

src/ngResource/resource.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,9 @@ angular.module('ngResource', ['ng']).
658658
httpConfig.timeout = timeoutDeferred.promise;
659659

660660
if (numericTimeout) {
661-
numericTimeoutPromise = $timeout(timeoutDeferred.resolve, numericTimeout);
661+
numericTimeoutPromise = $timeout(function() {
662+
timeoutDeferred.resolve("timeout");
663+
}, numericTimeout);
662664
}
663665
}
664666

@@ -729,7 +731,12 @@ angular.module('ngResource', ['ng']).
729731
// - return the instance / collection
730732
value.$promise = promise;
731733
value.$resolved = false;
732-
if (cancellable) value.$cancelRequest = timeoutDeferred.resolve;
734+
735+
if (cancellable) {
736+
value.$cancelRequest = function() {
737+
timeoutDeferred.resolve("cancelled");
738+
};
739+
}
733740

734741
return value;
735742
}

test/ng/httpBackendSpec.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,9 @@ describe('$httpBackend', function() {
173173

174174

175175
it('should abort request on timeout promise resolution', inject(function($timeout) {
176-
callback.andCallFake(function(status, response) {
176+
callback.andCallFake(function(status, response, headers, statusText) {
177177
expect(status).toBe(-1);
178+
expect(statusText).toBe('');
178179
});
179180

180181
$backend('GET', '/url', null, callback, {}, $timeout(noop, 2000));
@@ -190,6 +191,28 @@ describe('$httpBackend', function() {
190191
}));
191192

192193

194+
it('should abort request on timeout promise resolution with statusText', inject(function($timeout) {
195+
callback.andCallFake(function(status, response, headers, statusText) {
196+
expect(status).toBe(-1);
197+
expect(statusText).toBe('whatever');
198+
});
199+
200+
$backend('GET', '/url', null, callback, {}, $timeout(function() {
201+
return "whatever";
202+
}, 2000));
203+
204+
xhr = MockXhr.$$lastInstance;
205+
spyOn(xhr, 'abort');
206+
207+
$timeout.flush();
208+
expect(xhr.abort).toHaveBeenCalledOnce();
209+
210+
xhr.status = 0;
211+
xhr.onabort();
212+
expect(callback).toHaveBeenCalledOnce();
213+
}));
214+
215+
193216
it('should not abort resolved request on timeout promise resolution', inject(function($timeout) {
194217
callback.andCallFake(function(status, response) {
195218
expect(status).toBe(200);

test/ngMock/angular-mocksSpec.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1399,6 +1399,24 @@ describe('ngMock', function() {
13991399
});
14001400

14011401

1402+
it('should abort requests when timeout promise resolves with statusText', function() {
1403+
hb.expect('GET', '/url1').respond(200);
1404+
1405+
var canceler, then = jasmine.createSpy('then').andCallFake(function(fn) {
1406+
canceler = fn;
1407+
});
1408+
1409+
hb('GET', '/url1', null, callback, null, {then: then});
1410+
expect(typeof canceler).toBe('function');
1411+
1412+
canceler('whatever'); // simulate promise resolution
1413+
1414+
expect(callback).toHaveBeenCalledWith(-1, undefined, '', 'whatever');
1415+
hb.verifyNoOutstandingExpectation();
1416+
hb.verifyNoOutstandingRequest();
1417+
});
1418+
1419+
14021420
it('should abort requests when timeout passed as a numeric value', inject(function($timeout) {
14031421
hb.expect('GET', '/url1').respond(200);
14041422

test/ngResource/resourceSpec.js

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,7 +1544,13 @@ describe('cancelling requests', function() {
15441544
}
15451545
});
15461546

1547-
CreditCard.get();
1547+
var creditCard = CreditCard.get();
1548+
1549+
creditCard.$promise.catch(function(err) {
1550+
expect(err.status).toEqual(-1);
1551+
expect(err.statusText).toEqual("timeout");
1552+
});
1553+
15481554
$timeout.flush();
15491555
expect($httpBackend.flush).toThrow(new Error('No pending request to flush !'));
15501556

@@ -1563,7 +1569,14 @@ describe('cancelling requests', function() {
15631569
}
15641570
});
15651571

1566-
CreditCard.get().$cancelRequest();
1572+
var creditCard = CreditCard.get();
1573+
1574+
creditCard.$promise.catch(function(err) {
1575+
expect(err.status).toEqual(-1);
1576+
expect(err.statusText).toEqual("cancelled");
1577+
});
1578+
1579+
creditCard.$cancelRequest();
15671580
expect($httpBackend.flush).toThrow(new Error('No pending request to flush !'));
15681581

15691582
CreditCard.get();
@@ -1581,7 +1594,14 @@ describe('cancelling requests', function() {
15811594
}
15821595
});
15831596

1584-
CreditCard.get().$cancelRequest();
1597+
var creditCard = CreditCard.get();
1598+
1599+
creditCard.$promise.catch(function(err) {
1600+
expect(err.status).toEqual(-1);
1601+
expect(err.statusText).toEqual("cancelled");
1602+
});
1603+
1604+
creditCard.$cancelRequest();
15851605
expect($httpBackend.flush).toThrow(new Error('No pending request to flush !'));
15861606

15871607
CreditCard.get();

0 commit comments

Comments
 (0)