Skip to content

Commit

Permalink
Merge pull request #66 from daern91/master
Browse files Browse the repository at this point in the history
add initialJitter feature
  • Loading branch information
mdlavin authored Mar 21, 2024
2 parents 3394267 + 8ccea63 commit 2087db6
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 3 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ The following object shows the default options:
factor: 0,
timeout: 0,
jitter: false,
initialJitter: false,
handleError: null,
handleTimeout: null,
beforeAttempt: null,
Expand Down Expand Up @@ -155,6 +156,13 @@ to your target environment.

(default: `false`)

- **`initialJitter`**: `Boolean`

If `initialJitter` is `true` then a `jitter` will also be used in the
first call attempt.

(default: `false`)

- **`minDelay`**: `Number`

`minDelay` is used to set a lower bound of delay
Expand Down
12 changes: 9 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface AttemptOptions<T> {
readonly maxAttempts: number;
readonly timeout: number;
readonly jitter: boolean;
readonly initialJitter: boolean;
readonly handleError: HandleError<T> | null;
readonly handleTimeout: HandleTimeout<T> | null;
readonly beforeAttempt: BeforeAttempt<T> | null;
Expand All @@ -44,6 +45,7 @@ function applyDefaults<T> (options?: PartialAttemptOptions<T>): AttemptOptions<T
maxAttempts: (options.maxAttempts === undefined) ? 3 : options.maxAttempts,
timeout: (options.timeout === undefined) ? 0 : options.timeout,
jitter: (options.jitter === true),
initialJitter: (options.initialJitter === true),
handleError: (options.handleError === undefined) ? null : options.handleError,
handleTimeout: (options.handleTimeout === undefined) ? null : options.handleTimeout,
beforeAttempt: (options.beforeAttempt === undefined) ? null : options.beforeAttempt,
Expand All @@ -52,9 +54,7 @@ function applyDefaults<T> (options?: PartialAttemptOptions<T>): AttemptOptions<T
}

export async function sleep (delay: number) {
return new Promise((resolve, reject) => {
setTimeout(resolve, delay);
});
return new Promise((resolve) => setTimeout(resolve, delay));
}

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

if (context.attemptNum < 1 && options.initialJitter) {
const delay = calculateDelay(context, options);
if (delay) {
await sleep(delay);
}
}
return makeAttempt();
}
72 changes: 72 additions & 0 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ test('should be able to calculate delays', (t) => {
maxAttempts: 0,
timeout: 0,
jitter: false,
initialJitter: false,
handleError: null,
handleTimeout: null,
beforeAttempt: null,
Expand Down Expand Up @@ -87,6 +88,7 @@ test('should default to 3 attempts with 200 delay', async (t) => {
maxAttempts: 3,
timeout: 0,
jitter: false,
initialJitter: false,
handleError: null,
handleTimeout: null,
beforeAttempt: null,
Expand Down Expand Up @@ -334,6 +336,38 @@ test('should support jitter', async (t) => {
});
});

test('should support jitter with initialJitter', async (t) => {
let expectedDelays = [
0,
100,
200,
400,
800
];

let lastTime = Date.now();

return retry(async (context) => {
let newTime = Date.now();
let actualDelay = newTime - lastTime;
lastTime = newTime;

t.true(actualDelay <= (expectedDelays[context.attemptNum] + DELAY_TOLERANCE));

if (context.attemptsRemaining === 0) {
return 'success';
} else {
throw new Error('try again');
}
}, {
maxAttempts: expectedDelays.length,
delay: 100,
factor: 2,
jitter: true,
initialJitter: true
});
});

test('should support jitter with minDelay', async (t) => {
let expectedDelays = [
0,
Expand Down Expand Up @@ -371,6 +405,44 @@ test('should support jitter with minDelay', async (t) => {
});
});

test('should support jitter with minDelay and initialJitter', async (t) => {
let expectedDelays = [
0,
100,
200,
400,
800
];

let lastTime = Date.now();
const minDelay = 100;

return retry(async (context) => {
let newTime = Date.now();
let actualDelay = newTime - lastTime;
lastTime = newTime;

if (context.attemptNum > 0) {
t.true(actualDelay >= minDelay);
}

t.true(actualDelay <= (expectedDelays[context.attemptNum] + DELAY_TOLERANCE));

if (context.attemptsRemaining === 0) {
return 'success';
} else {
throw new Error('try again');
}
}, {
maxAttempts: expectedDelays.length,
delay: 100,
minDelay,
factor: 2,
jitter: true,
initialJitter: true
});
});

test('should detect invalid minDelay', async (t) => {
const err = await t.throws(retry(async (context) => {
throw new Error('should not get here');
Expand Down

0 comments on commit 2087db6

Please sign in to comment.