@kree4js/commons-retrier
v1.0.0
Published
Common Utils About Task Retrying
Maintainers
Readme
@kree4js/commons-retrier
Commons-retrier Library provides a Retrier to:
- Retry a task — run a task at intervals until it succeeds (or give up after max retries / timeout)
- Always run — run a task repeatedly regardless of success or failure
- Forever — run a task indefinitely, ignoring max retries and total timeout
Options
Times
The max number of task executions. When the task has been attempted this many times overall (whether it succeeded or failed in always mode), retrying stops.
Intervals
After each attempt, how long to wait before the next one?
- Minimum Interval — the lower bound for the delay
- Maximum Interval — the upper bound for the delay
- Change Policy — how the interval evolves between attempts:
- Fixed Interval Policy — same delay every time
- Fixed Increase Policy — add a constant to the delay each attempt
- Factor Increase Policy — multiply the delay by a factor each attempt
- Shuttle Policy — oscillate the delay between min and max
- Exponential Backoff with Jitter — double the delay each attempt, with random jitter
- Fixed Backoff with Jitter — fixed delay plus random jitter
- Linear Backoff with Jitter — linear increase plus random jitter
Timeout
After the total timeout elapses, the whole retry operation fails with the last error (or stops if in always / forever mode). Set to 0 or negative to disable.
TaskTimeout
Timeout for a single task execution. If the task takes longer than this, it is considered failed for that attempt.
Three Execution Modes
| Mode | Stops on success? | Stops on max retries? | Stops on total timeout? | Use case |
|------|------------------|----------------------|------------------------|----------|
| retry(task) / task(task) | ✅ Yes | ✅ Yes | ✅ Yes | Standard retry until success |
| always(task) | ❌ No, continues | ✅ Yes | ✅ Yes | Periodic task with max bounds |
| forever(task) | ❌ No, continues | ❌ No, infinite | ❌ No, infinite | Daemon / keep-alive loop |
Install
npm install @kree4js/commons-retrierUsage
1. Retry until success — retry(task)
import { Retrier } from '@kree4js/commons-retrier'
const retrier = Retrier.infinite() // no retry limit
.min(100) // min interval, 100ms
.max(300) // max interval, 300ms
.fixedIncrease(20) // increase interval by 20ms each attempt
// Task: throws (or rejects) to trigger a retry
const task = (retries, latency) => {
if (retries <= 3) {
throw new Error('Task failed')
}
return 'success' // succeed on the 4th try
}
try {
const result = await retrier.retry(task).start()
console.log(result) // → success
} catch (err) {
console.error(err)
}2. Always run — always(task)
import { Retrier } from '@kree4js/commons-retrier'
const retrier = Retrier.times(4) // max 4 total executions
.min(100) // min interval, 100ms
.max(300) // max interval, 300ms
.fixedIncrease(20) // increase interval by 20ms each attempt
.onSuccess((result, retries, latency) => {
console.log(`Attempt ${retries} succeeded:`, result)
})
.onFailure((err, retries, latency) => {
console.error(`Attempt ${retries} failed:`, err.message)
})
.onMaxRetries((nextRetries, maxRetries) => {
console.error(`Max retries reached: ${nextRetries} > ${maxRetries}`)
})
const task = (retries, latency) => {
if (retries <= 2) {
return `Latency ${latency}ms, succeeded at attempt ${retries}`
}
throw new Error(`Latency ${latency}ms, failed at attempt ${retries}`)
}
// `always().start()` resolves when the retrier stops (max retries / timeout / manual stop)
await retrier.always(task).start()
console.log('Finished all retries')
// → Latency 0ms, succeeded at attempt 1
// → Latency 105ms, succeeded at attempt 2
// → Latency 228ms, failed at attempt 3
// → Latency 370ms, failed at attempt 4
// → Max retries reached: 5 > 4
// → Finished all retries3. Forever — forever(task)
import { Retrier } from '@kree4js/commons-retrier'
const retrier = Retrier
.fixedInterval(5000) // run every 5s
.forever(async () => {
await doHealthCheck()
})
// `.forever().start()` resolves immediately; the task keeps running in background
await retrier.start()
// Stop it later:
// await retrier.stop()API Reference
Factory methods (static)
| Method | Description |
|--------|-------------|
| Retrier.naming(name) | Create a Retrier with a descriptive name |
| Retrier.infinite() | Create a Retrier with no max-retry limit |
| Retrier.times(n) | Create a Retrier with n as the max executions |
| Retrier.maxRetries(n) | Alias of times(n) |
| Retrier.min(ms) | Set minimum interval |
| Retrier.max(ms) | Set maximum interval |
| Retrier.range(min, max) | Set both min and max intervals |
| Retrier.fixedInterval(ms) | Fixed delay between attempts |
| Retrier.fixedIncrease(inc) | Add inc ms each attempt |
| Retrier.factorIncrease(factor) | Multiply delay by factor each attempt |
| Retrier.fixedBackoff(ms, jitter?) | Fixed delay + optional jitter |
| Retrier.linearBackoff(inc, jitter?) | Linear increase + optional jitter |
| Retrier.exponentialBackoff(jitter?) | Doubles each attempt + optional jitter |
| Retrier.shuttleInterval(step, jitter?) | Oscillates between min and max |
| Retrier.timeout(ms) | Total operation timeout |
| Retrier.taskTimeout(ms) | Per-task execution timeout |
| Retrier.start(task) | Shortcut: create + set task + start |
Chainable instance methods
| Method | Description |
|--------|-------------|
| .name(str) | Assign a name (for logging / debugging) |
| .infinite() | Disable max-retries limit (sets to Infinity) |
| .times(n) / .maxRetries(n) | Set max total executions |
| .min(ms) | Set minimum interval |
| .max(ms) | Set maximum interval |
| .range(min, max) | Set both min and max |
| .fixedInterval(ms) | Use fixed-interval policy |
| .fixedIncrease(inc) | Use fixed-increase policy |
| .factorIncrease(factor) | Use factor-increase policy |
| .fixedBackoff(ms, jitter?) | Use fixed-backoff policy |
| .linearBackoff(inc, jitter?) | Use linear-backoff policy |
| .exponentialBackoff(jitter?) | Use exponential-backoff policy |
| .shuttleInterval(step, jitter?) | Use shuttle-interval policy |
| .timeout(ms) | Set total operation timeout (<= 0 disables it) |
| .noTimeout() | Shorthand: disable total timeout |
| .taskTimeout(ms) | Set per-task execution timeout |
| .task(fn) / .retry(fn) | Set the task function and use "retry until success" mode |
| .always(fn, resetAfterSuccess?) | Use "always run" mode |
| .forever(fn, resetAfterSuccess?) | Use "forever run" mode (ignores max-retries and timeout) |
| .start() | Begin execution; returns a Promise |
| .stop(reason?) | Manually stop execution |
| .wakeup() | If the retrier is sleeping between attempts, wake it immediately |
| .clone() | Create a fresh Retrier with identical configuration (runtime state not copied) |
| .resetRetryPolicy() | Reset the policy's next-interval to the minimum |
Event callbacks
All callbacks return this for chaining.
| Callback | Arguments | Description |
|----------|-----------|-------------|
| .onStart(listener) | (startAt) | Fired when retrying begins |
| .onRetry(listener) | (retries, latency) | Fired before each attempt |
| .onSuccess(listener) | (result, retries, latency) | Fired after a successful attempt |
| .onFailure(listener) | (err, retries, latency) | Fired after a failed attempt |
| .onTimeout(listener) | (retries, latency, timeout) | Fired when the total timeout is exceeded |
| .onTaskTimeout(listener) | (retries, latency, taskTimeout) | Fired when a single task times out |
| .onMaxRetries(listener) | (nextRetries, maxRetries) | Fired when the max executions would be exceeded |
| .onStop(listener) | (reason?) | Fired when stop() is called |
| .onCompleted(listener) | (totalRetries, totalSpent) | Fired when all retrying has ended |
| .onError(listener) | (err) | General error listener |
Getters
| Property | Returns | Description |
|----------|---------|-------------|
| .running | boolean | Whether the retrier is currently executing |
| .retrierName | string | The assigned name |
| .totalTimeout | number | The total operation timeout in ms |
| .onceTimeout | number | The per-task timeout in ms |
| .maxTaskExecution | number | The max executions setting |
| .totalRetried | number | How many retries have happened (current - 1) |
| .policy | Policy | The current interval policy instance |
| .minInterval | number | Current policy's minimum interval |
| .maxInterval | number | Current policy's maximum interval |
| .nextInterval | number | Current policy's next interval value |
Event flow
start()
│
├── emit(Start)
│
└── loop:
├── emit(Retry, retries, latency)
├── execute task
│ ├── success → emit(Success, result, retries, latency)
│ │ └── (retry mode) → resolve(result) & break
│ │ └── (always/forever mode) → continue
│ │
│ └── failure → emit(Failure, err, retries, latency)
│ └── store lastError
│
├── task-timeout? → emit(TaskTimeout, ...)
│
├── total-timeout? → emit(Timeout, ...) & stop
├── stop requested? → stop
├── max-retries? → emit(MaxRetries, ...) & stop
│
└── sleep(nextDelay) → wakeup? → break sleep & continue
wait → continue after delay
│
└── emit(Completed, totalRetries, totalSpent)License
Apache-2.0
