Skip to content

Commit a6b400d

Browse files
author
Brendan Robert
committed
Wait handling cleanup to improve readability
1 parent c5a2d24 commit a6b400d

File tree

2 files changed

+30
-18
lines changed

2 files changed

+30
-18
lines changed

index.js

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,11 @@ function isResponseTimedOut(retryOptions) {
3939
* @param {RetryOptions} retryOptions whether or not to retry on all http error codes or just >500
4040
* @param {Object} error error object if the fetch request returned an error
4141
* @param {Object} response fetch call response
42+
* @param {Number} wait Amount of time we will wait before retrying next
4243
* @returns {Boolean} whether or not to retry the request
4344
*/
44-
function shouldRetry(retryOptions, error, response) {
45-
if (isResponseTimedOut(retryOptions)) {
45+
function shouldRetry(retryOptions, error, response, waitTime) {
46+
if (getTimeRemaining(retryOptions) < waitTime) {
4647
return false;
4748
} else if (retryOptions && retryOptions.retryOnHttpResponse) {
4849
return retryOptions.retryOnHttpResponse(response);
@@ -166,9 +167,9 @@ module.exports = async function (url, options) {
166167

167168
return new Promise(function (resolve, reject) {
168169
const wrappedFetch = async () => {
169-
let processLastResult = ()=>{};
170170
while (!isResponseTimedOut(retryOptions)) {
171171
++attempt;
172+
const waitTime = getRetryDelay(retryOptions);
172173

173174
let timeoutHandler;
174175
if (retryOptions.socketTimeout) {
@@ -178,39 +179,30 @@ module.exports = async function (url, options) {
178179
}
179180

180181
try {
181-
// console.log(`Fetching ${url} attempt ${attempt}`);
182182
const response = await fetch(url, options);
183-
processLastResult = ()=>resolve(response);
184183

185-
if (shouldRetry(retryOptions, null, response)) {
186-
console.error(`Retrying in ${retryOptions.retryInitialDelay} milliseconds, attempt ${attempt} failed (status ${response.status}): ${response.statusText}`);
184+
if (shouldRetry(retryOptions, null, response, waitTime)) {
185+
console.error(`Retrying in ${waitTime} milliseconds, attempt ${attempt} failed (status ${response.status}): ${response.statusText}`);
187186
} else {
188187
// response.timeout should reflect the actual timeout
189188
response.timeout = retryOptions.socketTimeout;
190189
return resolve(response);
191190
}
192191
} catch (error) {
193-
processLastResult = ()=>reject(error);
194192
const response = {status: error.code || 500, statusMessage : error.message || "Unknown server error"};
195-
if (!shouldRetry(retryOptions, error, response)) {
193+
if (!shouldRetry(retryOptions, error, response, waitTime)) {
196194
if (error.name === 'AbortError') {
197195
return reject(new FetchError(`network timeout at ${url}`, 'request-timeout'));
198196
} else {
199197
return reject(error);
200198
}
201199
}
202-
console.error(`Retrying in ${retryOptions.retryInitialDelay} milliseconds, attempt ${attempt} error: ${error.message}`);
200+
console.error(`Retrying in ${waitTime} milliseconds, attempt ${attempt} error: ${error.message}`);
203201
} finally {
204202
clearTimeout(timeoutHandler);
205203
}
206-
207204
// Fetch loop is about to repeat, delay as needed first.
208-
const waitTime = getRetryDelay(retryOptions);
209-
if (getTimeRemaining(retryOptions) < waitTime) {
210-
console.error(`Timeout during retry delay, returning last received response`);
211-
processLastResult();
212-
break;
213-
} else if (waitTime > 0) {
205+
if (waitTime > 0) {
214206
await new Promise(resolve => setTimeout(resolve, waitTime));
215207
}
216208
retryOptions.retryInitialDelay *= retryOptions.retryBackoff; // update retry interval

test/index.test.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,20 @@ const getPort = require('get-port');
2727
const FAKE_BASE_URL = 'https://fakeurl.com';
2828
const FAKE_PATH = '/image/test.png';
2929

30+
class Timer {
31+
constructor() {
32+
this.start = Date.now();
33+
}
34+
35+
get ellapsed() {
36+
return Date.now() - this.start;
37+
}
38+
39+
isBetween(v1, v2) {
40+
const answer = (this.ellapsed >= v1 && this.ellapsed <= v2);
41+
return answer;
42+
}
43+
}
3044

3145
describe('test `retryInit` function', () => {
3246
afterEach(() => {
@@ -365,13 +379,17 @@ describe('test fetch retry', () => {
365379
message: 'something awful happened',
366380
code: '503',
367381
});
382+
const timer = new Timer();
368383
try {
369-
await fetch(`${FAKE_BASE_URL}${FAKE_PATH}`, { method: 'GET', retryOptions: { retryMaxDuration: 800 } });
384+
await fetch(`${FAKE_BASE_URL}${FAKE_PATH}`, { method: 'GET', retryOptions: { retryMaxDuration: 700 } });
385+
assert.fail("Should have thrown an error!");
370386
} catch (e) {
371387
assert(nock.isDone());
372388
assert.strictEqual(e.message, 'request to https://fakeurl.com/image/test.png failed, reason: something awful happened');
373389
assert.strictEqual(e.code, '503');
374390
}
391+
console.log(`ellapsed: ${timer.ellapsed}`);
392+
assert.ok(timer.isBetween(300, 500), "Should have taken approximately 400ms");
375393
});
376394

377395
it('test retry timeout on 404 response', async () => {
@@ -397,13 +415,15 @@ describe('test fetch retry', () => {
397415
.get(FAKE_PATH)
398416
.twice()
399417
.reply(505);
418+
const timer = new Timer();
400419
const response = await fetch(`${FAKE_BASE_URL}${FAKE_PATH}`,
401420
{
402421
method: 'GET', retryOptions: {
403422
retryInitialDelay: 5000,
404423
retryMaxDuration: 1000
405424
}
406425
});
426+
assert.ok(timer.isBetween(0, 100), "Should have taken < 100ms");
407427
assert(!nock.isDone()); // should fail on first fetch call
408428
nock.cleanAll();
409429
assert.strictEqual(response.statusText, 'HTTP Version Not Supported');

0 commit comments

Comments
 (0)