diff --git a/readme.md b/readme.md index 669e530..5dd3ddd 100644 --- a/readme.md +++ b/readme.md @@ -514,10 +514,11 @@ const response1 = await retry(handleAll, { maxAttempts: 3 }) An [event emitter](#events) that fires when we retry a call, before any backoff. It's invoked with an object that includes: -- the `delay` we're going to wait before retrying, and; +- the `delay` we're going to wait before retrying, +- the `attempt` number of the upcoming retry, starting at `1`, and; - either a thrown error like `{ error: someError, delay: number }`, or an errorful result in an object like `{ value: someValue, delay: number }` when using [result filtering](#handleresulttypector-filter--policyorresulttypector-filter). -Useful for telemetry. Returns a dispable instance. +Useful for telemetry. Returns a disposable instance. ```js const listener = retry.onRetry(reason => console.log('retrying a function call:', reason)); @@ -557,7 +558,7 @@ listener.dispose(); ### `retry.onGiveUp(callback)` -An [event emitter](#events) that fires when we're no longer retrying a call and are giving up. It's invoked with either a thrown error in an object like `{ error: someError }`, or an errorful result in an object like `{ value: someValue }` when using [result filtering](#handleresulttypector-filter--policyorresulttypector-filter). Useful for telemetry. Returns a dispable instance. +An [event emitter](#events) that fires when we're no longer retrying a call and are giving up. It's invoked with either a thrown error in an object like `{ error: someError }`, or an errorful result in an object like `{ value: someValue }` when using [result filtering](#handleresulttypector-filter--policyorresulttypector-filter). Useful for telemetry. Returns a disposable instance. ```js const listener = retry.onGiveUp(reason => console.log('retrying a function call:', reason)); diff --git a/src/RetryPolicy.test.ts b/src/RetryPolicy.test.ts index be8fe91..16bf74d 100644 --- a/src/RetryPolicy.test.ts +++ b/src/RetryPolicy.test.ts @@ -178,4 +178,16 @@ describe('RetryPolicy', () => { await expect(policy.execute(s)).to.eventually.be.rejectedWith(MyErrorA); expect(onGiveUp).to.have.been.calledWith({ error: err }); }); + + it('provides the attempt to the onRetry callback', async () => { + const s = stub().throws(new MyErrorA()); + const attempts: number[] = []; + const policy = retry(handleAll, { maxAttempts: 3 }); + policy.onRetry(({ attempt }) => { + attempts.push(attempt); + }); + + await expect(policy.execute(s)).to.eventually.be.rejectedWith(MyErrorA); + expect(attempts).to.deep.equal([1, 2, 3]); + }); }); diff --git a/src/RetryPolicy.ts b/src/RetryPolicy.ts index 23fbfc1..4c23925 100644 --- a/src/RetryPolicy.ts +++ b/src/RetryPolicy.ts @@ -49,7 +49,9 @@ export class RetryPolicy implements IPolicy { declare readonly _altReturn: never; private readonly onGiveUpEmitter = new EventEmitter>(); - private readonly onRetryEmitter = new EventEmitter & { delay: number }>(); + private readonly onRetryEmitter = new EventEmitter< + FailureReason & { delay: number; attempt: number } + >(); /** * @inheritdoc @@ -112,7 +114,7 @@ export class RetryPolicy implements IPolicy { const delayPromise = delay(delayDuration, !!this.options.unref); // A little sneaky reordering here lets us use Sinon's fake timers // when we get an emission in our tests. - this.onRetryEmitter.emit({ ...result, delay: delayDuration }); + this.onRetryEmitter.emit({ ...result, delay: delayDuration, attempt: retries + 1 }); await delayPromise; continue; }