-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.js
180 lines (171 loc) · 5.67 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
//. # 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