diff options
Diffstat (limited to 'node_modules/exponential-backoff/src/delay')
6 files changed, 139 insertions, 0 deletions
diff --git a/node_modules/exponential-backoff/src/delay/always/always.delay.spec.ts b/node_modules/exponential-backoff/src/delay/always/always.delay.spec.ts new file mode 100644 index 0000000..8a08cd8 --- /dev/null +++ b/node_modules/exponential-backoff/src/delay/always/always.delay.spec.ts @@ -0,0 +1,65 @@ +import { AlwaysDelay } from "./always.delay"; +import { IBackOffOptions, getSanitizedOptions } from "../../options"; + +describe(AlwaysDelay.name, () => { + let options: IBackOffOptions; + let delay: AlwaysDelay; + + function initClass() { + delay = new AlwaysDelay(options); + } + + beforeEach(() => { + options = getSanitizedOptions({}); + initClass(); + jest.useFakeTimers(); + }); + + it(`when calling #apply, the delay is equal to the starting delay`, async () => { + const spy = jest.fn(); + delay.apply().then(spy); + jest.runTimersToTime(options.startingDelay); + await Promise.resolve(); + + expect(spy).toHaveBeenCalledTimes(1); + }); + + it(`when the attempt number is 1, when calling #apply, + the delay is equal to the starting delay multiplied by the time multiple`, async () => { + delay.setAttemptNumber(1); + + const spy = jest.fn(); + delay.apply().then(spy); + jest.runTimersToTime(options.startingDelay * options.timeMultiple); + await Promise.resolve(); + + expect(spy).toHaveBeenCalledTimes(1); + }); + + it(`when the attempt number is 2, when calling #apply, + the delay is equal to the starting delay multiplied by the time multiple raised by the attempt number`, async () => { + const attemptNumber = 2; + delay.setAttemptNumber(attemptNumber); + + const spy = jest.fn(); + delay.apply().then(spy); + jest.runTimersToTime( + options.startingDelay * Math.pow(options.timeMultiple, attemptNumber) + ); + await Promise.resolve(); + + expect(spy).toHaveBeenCalledTimes(1); + }); + + it(`when the #maxDelay is less than #startingDelay, when calling #apply, + the delay is equal to the #maxDelay`, async () => { + options.maxDelay = options.startingDelay - 1; + + const spy = jest.fn(); + delay.apply().then(spy); + jest.runTimersToTime(options.maxDelay); + await Promise.resolve(); + + expect(spy).toHaveBeenCalledTimes(1); + }); +}); diff --git a/node_modules/exponential-backoff/src/delay/always/always.delay.ts b/node_modules/exponential-backoff/src/delay/always/always.delay.ts new file mode 100644 index 0000000..ec86c62 --- /dev/null +++ b/node_modules/exponential-backoff/src/delay/always/always.delay.ts @@ -0,0 +1,3 @@ +import { Delay } from "../delay.base"; + +export class AlwaysDelay extends Delay {}
\ No newline at end of file diff --git a/node_modules/exponential-backoff/src/delay/delay.base.ts b/node_modules/exponential-backoff/src/delay/delay.base.ts new file mode 100644 index 0000000..bed90d6 --- /dev/null +++ b/node_modules/exponential-backoff/src/delay/delay.base.ts @@ -0,0 +1,34 @@ +import { IDelay } from "./delay.interface"; +import { IBackOffOptions } from "../options"; +import { JitterFactory } from "../jitter/jitter.factory"; + +export abstract class Delay implements IDelay { + protected attempt = 0; + constructor(private options: IBackOffOptions) {} + + public apply() { + return new Promise(resolve => setTimeout(resolve, this.jitteredDelay)); + } + + public setAttemptNumber(attempt: number) { + this.attempt = attempt; + } + + private get jitteredDelay() { + const jitter = JitterFactory(this.options); + return jitter(this.delay); + } + + private get delay() { + const constant = this.options.startingDelay; + const base = this.options.timeMultiple; + const power = this.numOfDelayedAttempts; + const delay = constant * Math.pow(base, power); + + return Math.min(delay, this.options.maxDelay); + } + + protected get numOfDelayedAttempts() { + return this.attempt; + } +} diff --git a/node_modules/exponential-backoff/src/delay/delay.factory.ts b/node_modules/exponential-backoff/src/delay/delay.factory.ts new file mode 100644 index 0000000..95e89fb --- /dev/null +++ b/node_modules/exponential-backoff/src/delay/delay.factory.ts @@ -0,0 +1,18 @@ +import { IBackOffOptions } from "../options"; +import { SkipFirstDelay } from "./skip-first/skip-first.delay"; +import { AlwaysDelay } from "./always/always.delay"; +import { IDelay } from "./delay.interface"; + +export function DelayFactory(options: IBackOffOptions, attempt: number): IDelay { + const delay = initDelayClass(options); + delay.setAttemptNumber(attempt); + return delay; +} + +function initDelayClass(options: IBackOffOptions) { + if (!options.delayFirstAttempt) { + return new SkipFirstDelay(options); + } + + return new AlwaysDelay(options); +}
\ No newline at end of file diff --git a/node_modules/exponential-backoff/src/delay/delay.interface.ts b/node_modules/exponential-backoff/src/delay/delay.interface.ts new file mode 100644 index 0000000..6ecc8da --- /dev/null +++ b/node_modules/exponential-backoff/src/delay/delay.interface.ts @@ -0,0 +1,4 @@ +export interface IDelay { + apply: () => Promise<unknown>; + setAttemptNumber: (attempt: number) => void; +} diff --git a/node_modules/exponential-backoff/src/delay/skip-first/skip-first.delay.ts b/node_modules/exponential-backoff/src/delay/skip-first/skip-first.delay.ts new file mode 100644 index 0000000..9f2b7f5 --- /dev/null +++ b/node_modules/exponential-backoff/src/delay/skip-first/skip-first.delay.ts @@ -0,0 +1,15 @@ +import { Delay } from "../delay.base"; + +export class SkipFirstDelay extends Delay { + public async apply() { + return this.isFirstAttempt ? true : super.apply(); + } + + private get isFirstAttempt() { + return this.attempt === 0; + } + + protected get numOfDelayedAttempts() { + return this.attempt - 1; + } +}
\ No newline at end of file |