| 1 | # tryer
|
|---|
| 2 |
|
|---|
| 3 | [](https://gitlab.com/philbooth/tryer/pipelines)
|
|---|
| 4 | [](https://www.npmjs.com/package/tryer)
|
|---|
| 5 | [](https://www.npmjs.com/package/tryer)
|
|---|
| 6 | [](https://opensource.org/licenses/MIT)
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 | Because everyone loves a tryer!
|
|---|
| 10 | Conditional
|
|---|
| 11 | and repeated
|
|---|
| 12 | function invocation
|
|---|
| 13 | for node
|
|---|
| 14 | and browser.
|
|---|
| 15 |
|
|---|
| 16 | * [Say what?](#say-what)
|
|---|
| 17 | * [What size is it?](#what-size-is-it)
|
|---|
| 18 | * [How do I install it?](#how-do-i-install-it)
|
|---|
| 19 | * [How do I use it?](#how-do-i-use-it)
|
|---|
| 20 | * [Loading the library](#loading-the-library)
|
|---|
| 21 | * [Calling the exported function](#calling-the-exported-function)
|
|---|
| 22 | * [Examples](#examples)
|
|---|
| 23 | * [How do I set up the dev environment?](#how-do-i-set-up-the-dev-environment)
|
|---|
| 24 | * [What license is it released under?](#what-license-is-it-released-under)
|
|---|
| 25 |
|
|---|
| 26 | ## Say what?
|
|---|
| 27 |
|
|---|
| 28 | Sometimes,
|
|---|
| 29 | you want to defer
|
|---|
| 30 | calling a function
|
|---|
| 31 | until a certain
|
|---|
| 32 | pre-requisite condition is met.
|
|---|
| 33 | Other times,
|
|---|
| 34 | you want to
|
|---|
| 35 | call a function
|
|---|
| 36 | repeatedly
|
|---|
| 37 | until some post-requisite condition
|
|---|
| 38 | is satisfied.
|
|---|
| 39 | Occasionally,
|
|---|
| 40 | you might even want
|
|---|
| 41 | to do both
|
|---|
| 42 | for the same function.
|
|---|
| 43 |
|
|---|
| 44 | To save you writing
|
|---|
| 45 | explicit conditions
|
|---|
| 46 | and loops
|
|---|
| 47 | on each of those occasions,
|
|---|
| 48 | `tryer` implements
|
|---|
| 49 | a predicate-based approach
|
|---|
| 50 | that hides the cruft
|
|---|
| 51 | behind a simple,
|
|---|
| 52 | functional interface.
|
|---|
| 53 |
|
|---|
| 54 | Additionally,
|
|---|
| 55 | it allows you to easily specify
|
|---|
| 56 | retry intervals
|
|---|
| 57 | and limits,
|
|---|
| 58 | so that your code
|
|---|
| 59 | doesn't hog the CPU.
|
|---|
| 60 | It also supports
|
|---|
| 61 | exponential backoff
|
|---|
| 62 | of retry intervals,
|
|---|
| 63 | which can be useful
|
|---|
| 64 | when handling
|
|---|
| 65 | indefinite error states
|
|---|
| 66 | such as network failure.
|
|---|
| 67 |
|
|---|
| 68 | ## What size is it?
|
|---|
| 69 |
|
|---|
| 70 | 5.6 kb unminified with comments, 1.1 kb minified, 0.5 kb minified + gzipped.
|
|---|
| 71 |
|
|---|
| 72 | ## How do I install it?
|
|---|
| 73 |
|
|---|
| 74 | Via npm:
|
|---|
| 75 |
|
|---|
| 76 | ```
|
|---|
| 77 | npm i tryer --save
|
|---|
| 78 | ```
|
|---|
| 79 |
|
|---|
| 80 | Or if you just want the git repo:
|
|---|
| 81 |
|
|---|
| 82 | ```
|
|---|
| 83 | git clone git@gitlab.com:philbooth/tryer.git
|
|---|
| 84 | ```
|
|---|
| 85 |
|
|---|
| 86 | ## How do I use it?
|
|---|
| 87 |
|
|---|
| 88 | ### Loading the library
|
|---|
| 89 |
|
|---|
| 90 | If you are running in
|
|---|
| 91 | Node.js
|
|---|
| 92 | or another CommonJS-style
|
|---|
| 93 | environment,
|
|---|
| 94 | you can `require`
|
|---|
| 95 | tryer like so:
|
|---|
| 96 |
|
|---|
| 97 | ```javascript
|
|---|
| 98 | const tryer = require('tryer');
|
|---|
| 99 | ```
|
|---|
| 100 |
|
|---|
| 101 | It also the supports
|
|---|
| 102 | the AMD-style format
|
|---|
| 103 | preferred by Require.js.
|
|---|
| 104 |
|
|---|
| 105 | If you are
|
|---|
| 106 | including `tryer`
|
|---|
| 107 | with an HTML `<script>` tag,
|
|---|
| 108 | or neither of the above environments
|
|---|
| 109 | are detected,
|
|---|
| 110 | it will be exported globally as `tryer`.
|
|---|
| 111 |
|
|---|
| 112 | ### Calling the exported function
|
|---|
| 113 |
|
|---|
| 114 | `tryer` is a function
|
|---|
| 115 | that can be invoked to
|
|---|
| 116 | call other functions
|
|---|
| 117 | conditionally and repeatedly,
|
|---|
| 118 | without the need for
|
|---|
| 119 | explicit `if` statements
|
|---|
| 120 | or loops in your own code.
|
|---|
| 121 |
|
|---|
| 122 | `tryer` takes one argument,
|
|---|
| 123 | an options object
|
|---|
| 124 | that supports
|
|---|
| 125 | the following properties:
|
|---|
| 126 |
|
|---|
| 127 | * `action`:
|
|---|
| 128 | The function that you want to invoke.
|
|---|
| 129 | If `action` returns a promise,
|
|---|
| 130 | iterations will not end
|
|---|
| 131 | until the promise is resolved or rejected.
|
|---|
| 132 | Alternatively,
|
|---|
| 133 | `action` may take a callback argument, `done`,
|
|---|
| 134 | to signal that it is asynchronous.
|
|---|
| 135 | In that case,
|
|---|
| 136 | you are responsible
|
|---|
| 137 | for calling `done`
|
|---|
| 138 | when the action is finished.
|
|---|
| 139 | If `action` is not set,
|
|---|
| 140 | it defaults to an empty function.
|
|---|
| 141 |
|
|---|
| 142 | * `when`:
|
|---|
| 143 | A predicate
|
|---|
| 144 | that tests the pre-condition
|
|---|
| 145 | for invoking `action`.
|
|---|
| 146 | Until `when` returns true
|
|---|
| 147 | (or a truthy value),
|
|---|
| 148 | `action` will not be called.
|
|---|
| 149 | Defaults to
|
|---|
| 150 | a function that immediately returns `true`.
|
|---|
| 151 |
|
|---|
| 152 | * `until`:
|
|---|
| 153 | A predicate
|
|---|
| 154 | that tests the post-condition
|
|---|
| 155 | for invoking `action`.
|
|---|
| 156 | After `until` returns true
|
|---|
| 157 | (or a truthy value),
|
|---|
| 158 | `action` will no longer be called.
|
|---|
| 159 | Defaults to
|
|---|
| 160 | a function that immediately returns `true`.
|
|---|
| 161 |
|
|---|
| 162 | * `fail`:
|
|---|
| 163 | The error handler.
|
|---|
| 164 | A function
|
|---|
| 165 | that will be called
|
|---|
| 166 | if `limit` falsey values
|
|---|
| 167 | are returned by `when` or `until`.
|
|---|
| 168 | Defaults to an empty function.
|
|---|
| 169 |
|
|---|
| 170 | * `pass`:
|
|---|
| 171 | Success handler.
|
|---|
| 172 | A function
|
|---|
| 173 | that will be called
|
|---|
| 174 | after `until` has returned truthily.
|
|---|
| 175 | Defaults to an empty function.
|
|---|
| 176 |
|
|---|
| 177 | * `limit`:
|
|---|
| 178 | Failure limit,
|
|---|
| 179 | representing the maximum number
|
|---|
| 180 | of falsey returns from `when` or `until`
|
|---|
| 181 | that will be permitted
|
|---|
| 182 | before invocation is deemed to have failed.
|
|---|
| 183 | A negative number
|
|---|
| 184 | indicates that the attempt
|
|---|
| 185 | should never fail,
|
|---|
| 186 | instead continuing
|
|---|
| 187 | for as long as `when` and `until`
|
|---|
| 188 | have returned truthy values.
|
|---|
| 189 | Defaults to `-1`.
|
|---|
| 190 |
|
|---|
| 191 | * `interval`:
|
|---|
| 192 | The retry interval,
|
|---|
| 193 | in milliseconds.
|
|---|
| 194 | A negative number indicates
|
|---|
| 195 | that each subsequent retry
|
|---|
| 196 | should wait for twice the interval
|
|---|
| 197 | from the preceding iteration
|
|---|
| 198 | (i.e. exponential backoff).
|
|---|
| 199 | The default value is `-1000`,
|
|---|
| 200 | signifying that
|
|---|
| 201 | the initial retry interval
|
|---|
| 202 | should be one second
|
|---|
| 203 | and that each subsequent attempt
|
|---|
| 204 | should wait for double the length
|
|---|
| 205 | of the previous interval.
|
|---|
| 206 |
|
|---|
| 207 | ### Examples
|
|---|
| 208 |
|
|---|
| 209 | ```javascript
|
|---|
| 210 | // Attempt to insert a database record, waiting until `db.isConnected`
|
|---|
| 211 | // before doing so. The retry interval is 1 second on each iteration
|
|---|
| 212 | // and the call will fail after 10 attempts.
|
|---|
| 213 | tryer({
|
|---|
| 214 | action: () => db.insert(record),
|
|---|
| 215 | when: () => db.isConnected,
|
|---|
| 216 | interval: 1000,
|
|---|
| 217 | limit: 10,
|
|---|
| 218 | fail () {
|
|---|
| 219 | log.error('No database connection, terminating.');
|
|---|
| 220 | process.exit(1);
|
|---|
| 221 | }
|
|---|
| 222 | });
|
|---|
| 223 | ```
|
|---|
| 224 |
|
|---|
| 225 | ```javascript
|
|---|
| 226 | // Attempt to send an email message, optionally retrying with
|
|---|
| 227 | // exponential backoff starting at 1 second. Continue to make
|
|---|
| 228 | // attempts indefinitely until the call succeeds.
|
|---|
| 229 | let sent = false;
|
|---|
| 230 | tryer({
|
|---|
| 231 | action (done) {
|
|---|
| 232 | smtp.send(email, error => {
|
|---|
| 233 | if (! error) {
|
|---|
| 234 | sent = true;
|
|---|
| 235 | }
|
|---|
| 236 | done();
|
|---|
| 237 | });
|
|---|
| 238 | },
|
|---|
| 239 | until: () => sent,
|
|---|
| 240 | interval: -1000,
|
|---|
| 241 | limit: -1
|
|---|
| 242 | });
|
|---|
| 243 | ```
|
|---|
| 244 |
|
|---|
| 245 | ```javascript
|
|---|
| 246 | // Poll a device at 30-second intervals, continuing indefinitely.
|
|---|
| 247 | tryer({
|
|---|
| 248 | action: () => device.poll().then(response => handle(response)),
|
|---|
| 249 | interval: 30000,
|
|---|
| 250 | limit: -1
|
|---|
| 251 | });
|
|---|
| 252 | ```
|
|---|
| 253 |
|
|---|
| 254 | ## How do I set up the dev environment?
|
|---|
| 255 |
|
|---|
| 256 | The dev environment relies on
|
|---|
| 257 | [Chai],
|
|---|
| 258 | [JSHint],
|
|---|
| 259 | [Mocha],
|
|---|
| 260 | [please-release-me],
|
|---|
| 261 | [spooks.js] and
|
|---|
| 262 | [UglifyJS].
|
|---|
| 263 | The source code is in
|
|---|
| 264 | `src/tryer.js`
|
|---|
| 265 | and the unit tests are in
|
|---|
| 266 | `test/unit.js`.
|
|---|
| 267 |
|
|---|
| 268 | To install the dependencies:
|
|---|
| 269 |
|
|---|
| 270 | ```
|
|---|
| 271 | npm i
|
|---|
| 272 | ```
|
|---|
| 273 |
|
|---|
| 274 | To run the tests:
|
|---|
| 275 |
|
|---|
| 276 | ```
|
|---|
| 277 | npm t
|
|---|
| 278 | ```
|
|---|
| 279 |
|
|---|
| 280 | To lint the code:
|
|---|
| 281 |
|
|---|
| 282 | ```
|
|---|
| 283 | npm run lint
|
|---|
| 284 | ```
|
|---|
| 285 |
|
|---|
| 286 | To regenerate the minified lib:
|
|---|
| 287 |
|
|---|
| 288 | ```
|
|---|
| 289 | npm run minify
|
|---|
| 290 | ```
|
|---|
| 291 |
|
|---|
| 292 | ## What license is it released under?
|
|---|
| 293 |
|
|---|
| 294 | [MIT](COPYING)
|
|---|
| 295 |
|
|---|
| 296 | [chai]: http://chaijs.com/
|
|---|
| 297 | [jshint]: http://jshint.com/
|
|---|
| 298 | [mocha]: http://mochajs.org/
|
|---|
| 299 | [please-release-me]: https://gitlab.com/philbooth/please-release-me
|
|---|
| 300 | [spooks.js]: https://gitlab.com/philbooth/spooks.js
|
|---|
| 301 | [uglifyjs]: http://lisperator.net/uglifyjs/
|
|---|
| 302 | [license]: COPYING
|
|---|
| 303 |
|
|---|