source: frontend/node_modules/tryer/src/tryer.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 11 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 5.6 KB
Line 
1// Conditional and repeated task invocation for node and browser.
2
3/*globals setTimeout, define, module */
4
5(function (globals) {
6 'use strict';
7
8 if (typeof define === 'function' && define.amd) {
9 define(function () {
10 return tryer;
11 });
12 } else if (typeof module !== 'undefined' && module !== null) {
13 module.exports = tryer;
14 } else {
15 globals.tryer = tryer;
16 }
17
18 // Public function `tryer`.
19 //
20 // Performs some action when pre-requisite conditions are met and/or until
21 // post-requisite conditions are satisfied.
22 //
23 // @option action {function} The function that you want to invoke. Defaults to `() => {}`.
24 // If `action` returns a promise, iterations will not end until
25 // the promise is resolved or rejected. Alternatively, `action`
26 // may take a callback argument, `done`, to signal that it is
27 // asynchronous. In that case, you are responsible for calling
28 // `done` when the action is finished.
29 //
30 // @option when {function} Predicate used to test pre-conditions. Should return `false`
31 // to postpone `action` or `true` to perform it. Defaults to
32 // `() => true`.
33 //
34 // @option until {function} Predicate used to test post-conditions. Should return `false`
35 // to retry `action` or `true` to terminate it. Defaults to
36 // `() => true`.
37 //
38 // @option fail {function} Callback to be invoked if `limit` tries are reached. Defaults
39 // to `() => {}`.
40 //
41 // @option pass {function} Callback to be invoked after `until` has returned truthily.
42 // Defaults to `() => {}`.
43 //
44 // @option interval {number} Retry interval in milliseconds. A negative number indicates
45 // that subsequent retries should wait for double the interval
46 // from the preceding iteration (exponential backoff). Defaults
47 // to -1000.
48 //
49 // @option limit {number} Maximum retry count, at which point the call fails and retries
50 // will cease. A negative number indicates that retries should
51 // continue indefinitely. Defaults to -1.
52 //
53 // @example
54 // tryer({
55 // when: () => db.isConnected,
56 // action: () => db.insert(user),
57 // fail () {
58 // log.error('No database connection, terminating.');
59 // process.exit(1);
60 // },
61 // interval: 1000,
62 // limit: 10
63 // });
64 //
65 // @example
66 // let sent = false;
67 // tryer({
68 // until: () => sent,
69 // action: done => {
70 // smtp.send(email, error => {
71 // if (! error) {
72 // sent = true;
73 // }
74 // done();
75 // });
76 // },
77 // pass: next,
78 // interval: -1000,
79 // limit: -1
80 // });
81 function tryer (options) {
82 options = normaliseOptions(options);
83
84 iterateWhen();
85
86 function iterateWhen () {
87 if (preRecur()) {
88 iterateUntil();
89 }
90 }
91
92 function preRecur () {
93 return conditionallyRecur('when', iterateWhen);
94 }
95
96 function conditionallyRecur (predicateKey, iterate) {
97 if (! options[predicateKey]()) {
98 incrementCount(options);
99
100 if (shouldFail(options)) {
101 options.fail();
102 } else {
103 recur(iterate, postIncrementInterval(options));
104 }
105
106 return false;
107 }
108
109 return true;
110 }
111
112 function iterateUntil () {
113 var result;
114
115 if (isActionSynchronous(options)) {
116 result = options.action();
117
118 if (result && isFunction(result.then)) {
119 return result.then(postRecur, postRecur);
120 }
121
122 return postRecur();
123 }
124
125 options.action(postRecur);
126 }
127
128 function postRecur () {
129 if (conditionallyRecur('until', iterateUntil)) {
130 options.pass();
131 }
132 }
133 }
134
135 function normaliseOptions (options) {
136 options = options || {};
137 return {
138 count: 0,
139 when: normalisePredicate(options.when),
140 until: normalisePredicate(options.until),
141 action: normaliseFunction(options.action),
142 fail: normaliseFunction(options.fail),
143 pass: normaliseFunction(options.pass),
144 interval: normaliseNumber(options.interval, -1000),
145 limit: normaliseNumber(options.limit, -1)
146 };
147 }
148
149 function normalisePredicate (fn) {
150 return normalise(fn, isFunction, yes);
151 }
152
153 function isFunction (fn) {
154 return typeof fn === 'function';
155 }
156
157 function yes () {
158 return true;
159 }
160
161 function normaliseFunction (fn) {
162 return normalise(fn, isFunction, nop);
163 }
164
165 function nop () {
166 }
167
168 function normalise (thing, predicate, defaultValue) {
169 if (predicate(thing)) {
170 return thing;
171 }
172
173 return defaultValue;
174 }
175
176 function normaliseNumber (number, defaultNumber) {
177 return normalise(number, isNumber, defaultNumber);
178 }
179
180 function isNumber (number) {
181 return typeof number === 'number' && number === number;
182 }
183
184 function isActionSynchronous (options) {
185 return options.action.length === 0;
186 }
187
188 function incrementCount (options) {
189 options.count += 1;
190 }
191
192 function shouldFail (options) {
193 return options.limit >= 0 && options.count >= options.limit;
194 }
195
196 function postIncrementInterval (options) {
197 var currentInterval = options.interval;
198
199 if (options.interval < 0) {
200 options.interval *= 2;
201 }
202
203 return currentInterval;
204 }
205
206 function recur (fn, interval) {
207 setTimeout(fn, Math.abs(interval));
208 }
209}(this));
210
Note: See TracBrowser for help on using the repository browser.