Skip to content

Commit 2087db6

Browse files
authored
Merge pull request #66 from daern91/master
add initialJitter feature
2 parents 3394267 + 8ccea63 commit 2087db6

File tree

3 files changed

+89
-3
lines changed

3 files changed

+89
-3
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ The following object shows the default options:
7373
factor: 0,
7474
timeout: 0,
7575
jitter: false,
76+
initialJitter: false,
7677
handleError: null,
7778
handleTimeout: null,
7879
beforeAttempt: null,
@@ -155,6 +156,13 @@ to your target environment.
155156

156157
(default: `false`)
157158

159+
- **`initialJitter`**: `Boolean`
160+
161+
If `initialJitter` is `true` then a `jitter` will also be used in the
162+
first call attempt.
163+
164+
(default: `false`)
165+
158166
- **`minDelay`**: `Number`
159167

160168
`minDelay` is used to set a lower bound of delay

src/index.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export interface AttemptOptions<T> {
2020
readonly maxAttempts: number;
2121
readonly timeout: number;
2222
readonly jitter: boolean;
23+
readonly initialJitter: boolean;
2324
readonly handleError: HandleError<T> | null;
2425
readonly handleTimeout: HandleTimeout<T> | null;
2526
readonly beforeAttempt: BeforeAttempt<T> | null;
@@ -44,6 +45,7 @@ function applyDefaults<T> (options?: PartialAttemptOptions<T>): AttemptOptions<T
4445
maxAttempts: (options.maxAttempts === undefined) ? 3 : options.maxAttempts,
4546
timeout: (options.timeout === undefined) ? 0 : options.timeout,
4647
jitter: (options.jitter === true),
48+
initialJitter: (options.initialJitter === true),
4749
handleError: (options.handleError === undefined) ? null : options.handleError,
4850
handleTimeout: (options.handleTimeout === undefined) ? null : options.handleTimeout,
4951
beforeAttempt: (options.beforeAttempt === undefined) ? null : options.beforeAttempt,
@@ -52,9 +54,7 @@ function applyDefaults<T> (options?: PartialAttemptOptions<T>): AttemptOptions<T
5254
}
5355

5456
export async function sleep (delay: number) {
55-
return new Promise((resolve, reject) => {
56-
setTimeout(resolve, delay);
57-
});
57+
return new Promise((resolve) => setTimeout(resolve, delay));
5858
}
5959

6060
export function defaultCalculateDelay<T> (context: AttemptContext, options: AttemptOptions<T>): number {
@@ -205,5 +205,11 @@ export async function retry<T> (
205205
await sleep(initialDelay);
206206
}
207207

208+
if (context.attemptNum < 1 && options.initialJitter) {
209+
const delay = calculateDelay(context, options);
210+
if (delay) {
211+
await sleep(delay);
212+
}
213+
}
208214
return makeAttempt();
209215
}

test/index.test.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ test('should be able to calculate delays', (t) => {
1818
maxAttempts: 0,
1919
timeout: 0,
2020
jitter: false,
21+
initialJitter: false,
2122
handleError: null,
2223
handleTimeout: null,
2324
beforeAttempt: null,
@@ -87,6 +88,7 @@ test('should default to 3 attempts with 200 delay', async (t) => {
8788
maxAttempts: 3,
8889
timeout: 0,
8990
jitter: false,
91+
initialJitter: false,
9092
handleError: null,
9193
handleTimeout: null,
9294
beforeAttempt: null,
@@ -334,6 +336,38 @@ test('should support jitter', async (t) => {
334336
});
335337
});
336338

339+
test('should support jitter with initialJitter', async (t) => {
340+
let expectedDelays = [
341+
0,
342+
100,
343+
200,
344+
400,
345+
800
346+
];
347+
348+
let lastTime = Date.now();
349+
350+
return retry(async (context) => {
351+
let newTime = Date.now();
352+
let actualDelay = newTime - lastTime;
353+
lastTime = newTime;
354+
355+
t.true(actualDelay <= (expectedDelays[context.attemptNum] + DELAY_TOLERANCE));
356+
357+
if (context.attemptsRemaining === 0) {
358+
return 'success';
359+
} else {
360+
throw new Error('try again');
361+
}
362+
}, {
363+
maxAttempts: expectedDelays.length,
364+
delay: 100,
365+
factor: 2,
366+
jitter: true,
367+
initialJitter: true
368+
});
369+
});
370+
337371
test('should support jitter with minDelay', async (t) => {
338372
let expectedDelays = [
339373
0,
@@ -371,6 +405,44 @@ test('should support jitter with minDelay', async (t) => {
371405
});
372406
});
373407

408+
test('should support jitter with minDelay and initialJitter', async (t) => {
409+
let expectedDelays = [
410+
0,
411+
100,
412+
200,
413+
400,
414+
800
415+
];
416+
417+
let lastTime = Date.now();
418+
const minDelay = 100;
419+
420+
return retry(async (context) => {
421+
let newTime = Date.now();
422+
let actualDelay = newTime - lastTime;
423+
lastTime = newTime;
424+
425+
if (context.attemptNum > 0) {
426+
t.true(actualDelay >= minDelay);
427+
}
428+
429+
t.true(actualDelay <= (expectedDelays[context.attemptNum] + DELAY_TOLERANCE));
430+
431+
if (context.attemptsRemaining === 0) {
432+
return 'success';
433+
} else {
434+
throw new Error('try again');
435+
}
436+
}, {
437+
maxAttempts: expectedDelays.length,
438+
delay: 100,
439+
minDelay,
440+
factor: 2,
441+
jitter: true,
442+
initialJitter: true
443+
});
444+
});
445+
374446
test('should detect invalid minDelay', async (t) => {
375447
const err = await t.throws(retry(async (context) => {
376448
throw new Error('should not get here');

0 commit comments

Comments
 (0)