A simple retry decorator for typescript with no dependency.
npm install typescript-retry-decoratorImport and use it. Retry for Promise is supported as long as the runtime has promise(nodejs/evergreen-browser).
This library supports both TypeScript's legacy ("experimental") decorators and TypeScript 5+ standard decorators.
- If your project uses legacy decorators, enable experimentalDecorators in tsconfig.json.
- If your project uses TypeScript 5+ standard decorators (no experimentalDecorators), it should work without extra config.
> TypeScript 5 note: this library also supports the new "standard decorators" call signature ((value, context)) at runtime. If you're using TS5+ standard decorators (without experimentalDecorators), it should work without extra config — see ts5-decorator.md for background.
ms to back off. If not set, then no wait |{ maxInterval: 2000, multiplier: 2 } | This is for the ExponentialBackOffPolicy backOff. For backoffStrategy, more details below |MaxAttemptsError by default. if this is set to true, the original exception would be thrown instead. |#### Exponential options
The exponentialOption allows you to fine-tune the exponential backoff strategy using several options:
- maxInterval: The maximum interval between two retries. The default value is 2000 ms.
- multiplier: The multiplier to use to generate the next backoff interval from the last one. The default value is 2.
- backoffStrategy: Optional. If specified, determines the strategy used to introduce "jitter" between retry intervals. For an explanation of the available strategies and why you might select one over the other, check out this article.
- ExponentialBackoffStrategy.FullJitter: The base backoff interval is multiplied by a random number between 0 and 1.
- ExponentialBackoffStrategy.EqualJitter: The backoff interval is (base interval / 2) + (random value between 0 and base interval / 2).
typescript
import { Retryable, BackOffPolicy } from 'typescript-retry-decorator';let count: number = 1;
class RetryExample {
@Retryable({ maxAttempts: 3 })
static async noDelayRetry() {
console.info(
Calling noDelayRetry for the ${count++} time at ${new Date().toLocaleTimeString()});
throw new Error('I failed!');
} @Retryable({
maxAttempts: 3,
value: [SyntaxError, ReferenceError]
})
static async noDelaySpecificRetry(): Promise {
console.info(
Calling noDelaySpecificRetry for the ${count++} time at ${new Date().toLocaleTimeString()});
throw new SyntaxError('I failed with SyntaxError!');
} @Retryable({
maxAttempts: 3,
backOff: 1000,
doRetry: (e: Error) => {
return e.message === 'Error: 429';
}
})
static async doRetry() {
console.info(
Calling doRetry for the ${count++} time at ${new Date().toLocaleTimeString()});
throw new Error('Error: 429');
} @Retryable({
maxAttempts: 3,
backOff: 1000,
doRetry: (e: Error) => {
return e.message === 'Error: 429';
}
})
static async doNotRetry() {
console.info(
Calling doNotRetry for the ${count++} time at ${new Date().toLocaleTimeString()});
throw new Error('Error: 404');
} @Retryable({
maxAttempts: 3,
backOffPolicy: BackOffPolicy.FixedBackOffPolicy,
backOff: 1000
})
static async fixedBackOffRetry() {
console.info(
Calling fixedBackOffRetry 1s for the ${count++} time at ${new Date().toLocaleTimeString()});
throw new Error('I failed!');
} @Retryable({
maxAttempts: 3,
backOffPolicy: BackOffPolicy.ExponentialBackOffPolicy,
backOff: 1000,
exponentialOption: { maxInterval: 4000, multiplier: 3 }
})
static async ExponentialBackOffRetry() {
console.info(
Calling ExponentialBackOffRetry backOff 1s, multiplier=3 for the ${count++} time at ${new Date().toLocaleTimeString()});
throw new Error('I failed!');
} @Retryable({
maxAttempts: 3,
backOffPolicy: BackOffPolicy.ExponentialBackOffPolicy,
backOff: 1000,
exponentialOption: { maxInterval: 4000, multiplier: 2, backoffStrategy: ExponentialBackoffStrategy.EqualJitter }
})
static async ExponentialBackOffWithJitterRetry() {
console.info(
Calling ExponentialBackOffWithJitterRetry backOff 1s, multiplier=2 for the ${count++} time at ${new Date().toLocaleTimeString()});
throw new Error('I failed!');
}
}(async () => {
try {
resetCount();
await RetryExample.noDelayRetry();
} catch (e) {
console.info(
All retry done as expected, final message: '${e.message}');
} try {
resetCount();
await RetryExample.doRetry();
} catch (e) {
console.info(
All retry done as expected, final message: '${e.message}');
} try {
resetCount();
await RetryExample.doNotRetry();
} catch (e) {
console.info(
All retry done as expected, final message: '${e.message}');
} try {
resetCount();
await RetryExample.fixedBackOffRetry();
} catch (e) {
console.info(
All retry done as expected, final message: '${e.message}');
} try {
resetCount();
await RetryExample.ExponentialBackOffRetry();
} catch (e) {
console.info(
All retry done as expected, final message: '${e.message}');
} try {
resetCount();
await RetryExample.ExponentialBackOffWithJitterRetry();
} catch (e) {
console.info(
All retry done as expected, final message: '${e.message}');
}})();
function resetCount() {
count = 1;
}
`Run the above code with
ts-node, then output will be:
`
Calling noDelayRetry for the 1 time at 4:12:49 PM
Calling noDelayRetry for the 2 time at 4:12:49 PM
Calling noDelayRetry for the 3 time at 4:12:49 PM
Calling noDelayRetry for the 4 time at 4:12:49 PM
I failed!
All retry done as expected, final message: 'Failed for 'noDelayRetry' for 3 times.'
Calling noDelayRetry for the 1 time at 4:12:49 PM
Calling noDelayRetry for the 2 time at 4:12:49 PM
Calling noDelayRetry for the 3 time at 4:12:49 PM
Calling noDelayRetry for the 4 time at 4:12:49 PM
I failed with SyntaxError!
All retry done as expected, final message: 'Failed for 'noDelaySpecificRetry' for 3 times.'
Calling doRetry for the 1 time at 4:12:49 PM
Calling doRetry for the 2 time at 4:12:50 PM
Calling doRetry for the 3 time at 4:12:51 PM
Calling doRetry for the 4 time at 4:12:52 PM
Error: 429
All retry done as expected, final message: 'Failed for 'doRetry' for 3 times.'
Calling doNotRetry for the 1 time at 4:12:52 PM
All retry done as expected, final message: 'Error: 404'
Calling fixedBackOffRetry 1s for the 1 time at 4:12:52 PM
Calling fixedBackOffRetry 1s for the 2 time at 4:12:53 PM
Calling fixedBackOffRetry 1s for the 3 time at 4:12:54 PM
Calling fixedBackOffRetry 1s for the 4 time at 4:12:55 PM
I failed!
All retry done as expected, final message: 'Failed for 'fixedBackOffRetry' for 3 times.'
Calling ExponentialBackOffRetry backOff 1s, multiplier=3 for the 1 time at 4:12:55 PM
Calling ExponentialBackOffRetry backOff 1s, multiplier=3 for the 2 time at 4:12:56 PM
Calling ExponentialBackOffRetry backOff 1s, multiplier=3 for the 3 time at 4:12:59 PM
Calling ExponentialBackOffRetry backOff 1s, multiplier=3 for the 4 time at 4:13:03 PM
I failed!
All retry done as expected, final message: 'Failed for 'ExponentialBackOffRetry' for 3 times.'
Calling ExponentialBackOffWithJitterRetry backOff 1s, multiplier=2 for the 1 time at 4:13:03 PM
Calling ExponentialBackOffWithJitterRetry backOff 1s, multiplier=2 for the 2 time at 4:13:03 PM
Calling ExponentialBackOffWithJitterRetry backOff 1s, multiplier=2 for the 3 time at 4:13:05 PM
Calling ExponentialBackOffWithJitterRetry backOff 1s, multiplier=2 for the 4 time at 4:13:09 PM
I failed!
All retry done as expected, final message: 'Failed for 'ExponentialBackOffWithJitterRetry' for 3 times.'
``