{"id":15984532,"url":"https://github.com/metcoder95/one-more-time","last_synced_at":"2025-09-25T09:44:53.309Z","repository":{"id":191045955,"uuid":"683799222","full_name":"metcoder95/one-more-time","owner":"metcoder95","description":"Retry library with focus on flow control","archived":false,"fork":false,"pushed_at":"2024-08-20T06:48:39.000Z","size":37,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-10-17T03:36:06.485Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/metcoder95.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-08-27T18:24:36.000Z","updated_at":"2024-07-31T10:19:03.000Z","dependencies_parsed_at":null,"dependency_job_id":"3c4ab061-e4e7-4ed5-823a-0a0a838ea1d2","html_url":"https://github.com/metcoder95/one-more-time","commit_stats":{"total_commits":18,"total_committers":2,"mean_commits":9.0,"dds":0.4444444444444444,"last_synced_commit":"b0a2c41d5ddd49386bdd02b5ff4a338b668564ea"},"previous_names":["metcoder95/one-more-time"],"tags_count":2,"template":false,"template_full_name":"metcoder95/lib_template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metcoder95%2Fone-more-time","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metcoder95%2Fone-more-time/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metcoder95%2Fone-more-time/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metcoder95%2Fone-more-time/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/metcoder95","download_url":"https://codeload.github.com/metcoder95/one-more-time/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244134020,"owners_count":20403537,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-08T02:09:26.067Z","updated_at":"2025-09-25T09:44:48.283Z","avatar_url":"https://github.com/metcoder95.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# One_More_Time\n\n\u003e A retry management library with a focus on flow control\n\nThis small library aims to provide granular control over the flow of retries. It is designed to be used in a functional style, and to be as unobtrusive as possible. It is also designed to be used with promises, and be aborted at any time through `AbortController` mechanism.\n\n## It can be used in either of two ways:\n\n### 1. As a wrapper\n\nActing as a wrapper over a passed function, this is the most common way and offloads all the `retry` control to the library. A promise is returned that will be resolved with the result of the wrapped function, or rejected with the last error that caused the retry to fail.\n\nTo decide when to retry, the library accepts as argument a function that returns a boolean. This function is called with the error that caused the retry. If the function returns `true`, the library will retry the wrapped function. If it returns `false`, the library will reject the promise with the last error that caused the retry to fail.\n\n```javascript\nconst { Retry } = require('one_more_time');\n\nlet counter = 0;\nconst foo = () =\u003e {\n  counter++;\n\n  if (counter !== 3) {\n    throw new Error('foo');\n  } else {\n    return 'bar';\n  }\n};\n\nconst shouldRetry = (err) =\u003e {\n  return true;\n};\n\nconst retry = new Retry();\n\nconst result = await retry.run(foo, { shouldRetry });\n```\n\n### 2. As a management tool\n\nThis enables the most control over the flow of the retry. It returns a `task` which is an instance of a `Task` class that aims to reprent an individual logic unit and its retry management. It is up to the user to decide when to retry, and when to stop retrying.\n\nIt exposes a `timeout` method that allows the user to wait for a given amount of time before retrying. It also exposes a `shouldRetry` method that can be used to decide whether to retry or not.\n\nA `task` can be used as long as needed, but once is either `exhausted` or `aborted`, it needs to be reset for subsequent usage.\n\n```javascript\nconst { Retry } = require('one_more_time');\nconst fn = () =\u003e {\n  counter++;\n\n  if (counter \u003c 4) {\n    throw new Error('I failed!');\n  }\n};\n\nwhile (true) {\n  try {\n    fn();\n    break;\n  } catch (err) {\n    if (task.shouldRetry(err)) {\n      await task.timeout();\n      continue;\n    }\n\n    throw err;\n  }\n}\n```\n\n## Setup\n\n### Installation\n\nEasy to install as:\n\n\u003e `npm install one_more_time`\n\n### Retry\n\n```ts\n{\n  /**\n   * The number of times to retry the operation. In ms\n   *\n   * @type {number}\n   * @default 3\n   */\n  retries?: number;\n  /**\n   * The exponential factor to use.\n   * 2 means the first delay is 2 times the base, the second 2^2 times the base,\n   * the third 2^3 times the base, and so on.\n   *\n   * @type {number}\n   * @default 2\n   */\n  factor?: number;\n  /**\n   * The number of milliseconds before starting the first retry.\n   *\n   * @type {number}\n   * @default 500\n   */\n  minTimeout?: number;\n  /**\n   * The maximum number of milliseconds between two retries.\n   *\n   * @type {number}\n   * @default 3000\n   */\n  maxTimeout?: number;\n}\n```\n\n##### `Retry#run`\n\nActing as a wrapper over a passed function, this is the most common way and offloads all the `retry` control to the library.\nA promise is returned that will be resolved with the result of the wrapped function, or rejected with the last error that caused the retry to fail.\n\n**Arguments**\n\n- `fn` - **required** - The function to wrap\n- `options` - **optional** - The options to use for the retry, it enables to set a default state over the `task` that will be used to manage the given wrapped function. It also can be used to pass a `signal` to abort the retry at any time.\n  - `id` - **optional** - The id to use for the task. If not provided, a random id will be generated.\n  - `retries` - **optional** - The number of remaining retries for the operation. If not provided, will default to `0`.\n  - `signal` - **optional** - The signal to use to abort the retry.\n  - `currentTimeout` - **optional** - The current timeout to use for the retry. If not provided, will default to `minTimeout` passed when instantiating a `retry`.\n  - `shouldRetry` - **optional** - The function to use to decide whether to retry or not. If not provided, will default to `() =\u003e true`.\n\n**Example**\n\n```js\nconst shouldRetry = (err) =\u003e {\n  return true;\n};\n\nconst foo = () =\u003e {\n  // do something\n};\n\nconst retry = new Retry();\n\nconst result = await retry.run(foo, { shouldRetry });\n```\n\n##### `Retry#pick`\n\nIt returns a `task` which is an instance of a `Task` class that aims to reprent an individual logic unit and its retry management.\n\n**Arguments**\n\n- `options` - **optional** - The options to use for the retry, it enables to set a default state over the `task` that will be used to manage the given wrapped function. It also can be used to pass a `signal` to abort the retry at any time.\n  - `id` - **optional** - The id to use for the task. If not provided, a random id will be generated.\n  - `retries` - **optional** - The number of remaining retries for the operation. If not provided, will default to `0`.\n  - `signal` - **optional** - The signal to use to abort the retry.\n  - `currentTimeout` - **optional** - The current timeout to use for the retry. If not provided, will default to `minTimeout` passed when instantiating a `retry`.\n\n**Example**\n\n```js\nconst fn = () =\u003e {\n  // do something\n};\n\nconst retry = new Retry();\nconst task = retry.pick();\n\nwhile (true) {\n  try {\n    fn();\n    break;\n  } catch (err) {\n    if (task.shouldRetry(err)) {\n      await task.timeout();\n      continue;\n    }\n\n    throw err;\n  }\n}\n```\n\n### Task\n\nIt represents an individual logic unit and its retry management. It is up to the user to decide when to retry, and when to stop retrying.\n\n#### `Task#timeout`\n\nIt returns a promise that will be resolved after a given timeout.\nIt is calculated accordingly to the following formula:\n\n```js\nconst timeout = Math.min(maxTimeout, currentTimeout * factor ** retries);\n```\n\nThe promise can be `rejected` if the `signal` passed to the `task` is aborted.\n\n#### `Task#shouldRetry`\n\nIt returns a boolean that indicates whether the task should retry or not, accordingly to the settings set for the `retry` instance, and the current state of the `task`.\n\nIt automatically returns `false` if the `retries` are exhausted, the `signal` is aborted, or the `err` is falsy.\n\n**Arguments**\n\n- `err` - **required** - The error that caused the retry.\n\n#### `Task#reset`\n\nIt resets the `task` to its initial state for reusage.\n\n#### `Task#start`\n\nIt starts the `task` by executing internal preparations to its state.\nIt accepts a `signal` for subsequent usage to abort the `task`.\n\nIf called when the `task` is already started (already in used or exhausted), it will throw an error.\n\n**Arguments**\n\n- `signal` - **optional** - The signal to use to abort the retry.\n\n#### `Task.prototype`\n\n- `id` - **readonly** - The id of the `task`.\n- `retries` - **readonly** - The number of remaining retries for the operation.\n- `signal` - **readonly** - The signal to use to abort the retry.\n- `history` - **readonly** - The history of errors of the `task`.\n- `rootError` - **readonly** - The root error of the `task`.\n- `aborted` - **readonly** - Whether the `task` is aborted or not.\n\n\n### License\n\n- **MIT** : http://opensource.org/licenses/MIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetcoder95%2Fone-more-time","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmetcoder95%2Fone-more-time","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetcoder95%2Fone-more-time/lists"}