UNPKG

true-myth

Version:

A library for safe functional programming in JavaScript, with first-class support for TypeScript

73 lines (62 loc) 3.37 kB
# Task > Delay Types and helpers for managing retry delays with the `withRetries` helper from `Task`. The `withRetries` function accepts two arguments: - A callback that produces a `Task` or `StopRetrying`, an `Error` subclass that acts as a “sentinel” value for. - A retry delay strategy, which is an iterable iterator of delay times. When the callback produces a resolved `Task`, the `Task` returned from `withRetries` resolves to that value and all previous rejections are dropped. When the callback produces a rejected `Task`, `withRetries` retries the callback using the delay until the retry strategy is exhausted, at which point `withRetries` will produce a `Task` that rejects with a `RetryFailure`, which will have all rejection values. The strategies provided in this module represent the *most common*, but definitely not the *only possible* strategies you can use for retrying. - `exponential` - `fibonacci` - `fixed` - `immediate` - `linear` - `none` Additionally, the `jitter` function provides a useful tool for generating random variations on a retry strategy, to help avoid [“thundering herd” problems][thp] where many tasks kick off at the same time, fail at the same time, and then retry at the same time, causing increasing load on a resource that is already failing. [thp]: https://en.wikipedia.org/wiki/Thundering_herd_problem You should make sure you understand the tradeoffs of each backoff strategy. > [!NOTE] > All the helpers in this module except `none` are infinite, so you almost certainly want to use another helper to stop after a number of retries or to use the `count` passed as an argument to do the same. > > **Recommended:** Update to at least TS 5.6+, or use a TypeScript-aware polyfill for the Iterator Helpers feature (ES2025). In that case, you can simply use the `take` method directly: > > ```ts > import * as Task from 'true-myth/task'; > import * as Delay from 'true-myth/task/delay'; > > let theTask = Task.withRetries( > () => Task.fromPromise(fetch('https://example.com/')), > Delay.exponential().map(Delay.jitter).take(5), > ); > ``` > > **Fallback:** If you are unable to use a polyfill or upgrade to TS 5.6 for now, you can still use these safely using generator functions, which are long-standing JavaScript features available in all modern browsers since ES6. The above example might be written like this (note that these are fully-general versions of the `take` and `map` functions—that is, much more general than is required for working with the “strategies” from True Myth). > > ```ts > import * as Task from 'true-myth/task'; > import { exponential, jitter } from 'true-myth/task/delay'; > > let theTask = Task.withRetries( > () => Task.fromPromise(fetch('https://example.com/')), > take(map(Delay.exponential(), Delay.jitter), 5), > ); > > function* take<T>(iterable: Iterable<T>, count: number): Generator<T> { > let taken = 0; > for (let item of iterable) { > if (taken >= count) { > break; > } > > taken += 1; > yield item; > } > } > > function* map<T, U>(iterable: Iterable<T>, fn: (t: T) => U): Generator<U> { > for (let value of iterable) { > yield fn(value); > } > } > ``` > > This is a bit harder to follow but works the same way, and will let you migrate incrementally once you are able to use the ES2025 Iterator Helpers features.