UNPKG

fluture-retry

Version:

Toolset for retrying potentially failing computations

181 lines (171 loc) 5.8 kB
//. # Fluture retry //. //. Toolset for retrying potentially failing computations represented by //. [Fluture][] Futures. //. //. ## Usage //. //. ### Node //. //. ```console //. $ npm install --save fluture fluture-retry //. ``` //. //. On Node 12 and up, this module can be loaded directly with `import` or //. `require`. On Node versions below 12, `require` or the [esm][]-loader can //. be used. //. //. ### Deno and Modern Browsers //. //. You can load the EcmaScript module from various content delivery networks: //. //. - [Skypack](https://cdn.skypack.dev/fluture-retry@3.1.0) //. - [JSPM](https://jspm.dev/fluture-retry@3.1.0) //. - [jsDelivr](https://cdn.jsdelivr.net/npm/fluture-retry@3.1.0/+esm) //. //. ### Old Browsers and Code Pens //. //. There's a [UMD][] file included in the NPM package, also available via //. jsDelivr: https://cdn.jsdelivr.net/npm/fluture-retry@3.1.0/dist/umd.js //. //. This file adds `flutureRetry` to the global scope, or use CommonJS/AMD //. when available. //. //. ### Usage Example //. //. Let's say we have the following `Future Error String` that may fail //. occasionally: //. //. ```js //. import {Future} from 'fluture'; //. const task = Future ((rej, res) => { //. const fail = Math.random () > 0.8; //. setTimeout (fail ? rej : res, 100, fail ? new Error ('rej') : 'res'); //. }); //. ``` //. //. We might simply want to try again when it does fail, a certain amount of //. times, waiting a certain length of time in between tries. //. //. #### Basic usage //. //. The `retryLinearly` export will take a Future and produce a Future which //. retries the computation five times, at linearly increasing intervals //. (1 second, 2 seconds, 3 seconds, etc). If all tries fail, the Future //. rejects with the last encountered rejection reason. So we can simply wrap //. our `task` from before, and we get back a `Future Error String` with //. increased odds of success. //. //. ```js //. import {fork} from 'fluture'; //. import {retryLinearly} from 'fluture-retry'; //. const retriedTask = retryLinearly (task); //. fork (retriedTask) (console.error) (console.log); //. ``` //. //. #### Advanced usage //. //. The pre-baked retry strategies may not include exactly what you need. The //. `retry` function puts you in control of the following: //. //. * How much time is in between every try, and how the amount of failures //. affect the waiting time. //. * How many times a Future is retried. //. * What to do with the accumulated errors. //. //. In the following example, we retry our task 32 times, with an exponentially //. increasing interval starting at 64ms. It will have retried 32 times after //. about two and a half minutes, waiting just over a minute at most. At the //. end we list all unique error messages. //. //. ```js //. import {fork} from 'fluture'; //. import {retry, exponentially} from 'fluture-retry'; //. //. // retriedTask :: Future (Array Error) String //. const retriedTask = retry (exponentially (64)) (32) (task); //. //. fork (retriedTask) ( //. errors => console.error ( //. `All tries failed. The following errors were encountered: \n ${ //. Array.from ( //. new Set (errors.map (({message}) => message)) //. ).join ('\n ') //. }.` //. ) //. ) (console.log); //. ``` import {reject, after, mapRej, chain, chainRej} from 'fluture/index.js'; // last :: NonEmpty (Array a) -> a function last(xs) { return xs[xs.length - 1]; } //. ## API //. //# retry :: (Number -> Number) -> Number -> Future a b -> Future (Array a) b //. //. Create a retrying Future using the given parameters: //. //. 1. A function over the amount of failures to determine waiting time. //. See [`exponentially`](#exponentially), [`linearly`](#linearly) and //. [`statically`](#statically) for pre-baked functions of this sort. //. 1. The maximum number of retries before failing. //. 1. A Future representing the computation to retry. //. //. See [Advanced usage](#advanced-usage) for an example. export function retry(time) { return function retry(max) { return function retry(task) { var failures = new Array (max); return (function recur(i) { return task.pipe (chainRej (function(failure) { failures[i] = failure; var total = i + 1; return total === max ? reject (failures) : chain (recur) (after (time (total)) (total)); })); } (0)); }; }; } //# exponentially :: Number -> Number -> Number //. //. Takes two numbers and returns the result of multiplying the first by //. the second raised to the power of two. To be partially applied and used //. as a first argument to `retry`. export function exponentially(t) { return function exponentially(n) { return t * Math.pow (n, 2); }; } //# linearly :: Number -> Number -> Number //. //. Takes two numbers and returns the result of multiplying them. To be //. partially applied and used as a first argument to `retry`. export function linearly(t) { return function linearly(n) { return t * n; }; } //# statically :: a -> b -> a //. //. Takes two values and returns the first. To be partially applied and used //. as a first argument to `retry`. export function statically(t) { return function statically(_) { return t; }; } //# linearSeconds :: Number -> Number //. //. Takes a number and multiplies it by 1000. export var linearSeconds = linearly (1000); //# retryLinearly :: Future a b -> Future a b //. //. A pre-baked retry strategy. See [Basic usage](#basic-usage). export function retryLinearly(task) { return mapRej (last) (retry (linearSeconds) (5) (task)); } //. [Fluture]: https://github.com/fluture-js/Fluture //. [esm]: https://github.com/standard-things/esm //. [UMD]: https://github.com/umdjs/umd