npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kree4js/commons-retrier

v1.0.0

Published

Common Utils About Task Retrying

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-retrier

Usage

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 retries

3. 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