chushihua
This commit is contained in:
+210
@@ -0,0 +1,210 @@
|
||||
// Conditional and repeated task invocation for node and browser.
|
||||
|
||||
/*globals setTimeout, define, module */
|
||||
|
||||
(function (globals) {
|
||||
'use strict';
|
||||
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(function () {
|
||||
return tryer;
|
||||
});
|
||||
} else if (typeof module !== 'undefined' && module !== null) {
|
||||
module.exports = tryer;
|
||||
} else {
|
||||
globals.tryer = tryer;
|
||||
}
|
||||
|
||||
// Public function `tryer`.
|
||||
//
|
||||
// Performs some action when pre-requisite conditions are met and/or until
|
||||
// post-requisite conditions are satisfied.
|
||||
//
|
||||
// @option action {function} The function that you want to invoke. Defaults to `() => {}`.
|
||||
// If `action` returns a promise, iterations will not end until
|
||||
// the promise is resolved or rejected. Alternatively, `action`
|
||||
// may take a callback argument, `done`, to signal that it is
|
||||
// asynchronous. In that case, you are responsible for calling
|
||||
// `done` when the action is finished.
|
||||
//
|
||||
// @option when {function} Predicate used to test pre-conditions. Should return `false`
|
||||
// to postpone `action` or `true` to perform it. Defaults to
|
||||
// `() => true`.
|
||||
//
|
||||
// @option until {function} Predicate used to test post-conditions. Should return `false`
|
||||
// to retry `action` or `true` to terminate it. Defaults to
|
||||
// `() => true`.
|
||||
//
|
||||
// @option fail {function} Callback to be invoked if `limit` tries are reached. Defaults
|
||||
// to `() => {}`.
|
||||
//
|
||||
// @option pass {function} Callback to be invoked after `until` has returned truthily.
|
||||
// Defaults to `() => {}`.
|
||||
//
|
||||
// @option interval {number} Retry interval in milliseconds. A negative number indicates
|
||||
// that subsequent retries should wait for double the interval
|
||||
// from the preceding iteration (exponential backoff). Defaults
|
||||
// to -1000.
|
||||
//
|
||||
// @option limit {number} Maximum retry count, at which point the call fails and retries
|
||||
// will cease. A negative number indicates that retries should
|
||||
// continue indefinitely. Defaults to -1.
|
||||
//
|
||||
// @example
|
||||
// tryer({
|
||||
// when: () => db.isConnected,
|
||||
// action: () => db.insert(user),
|
||||
// fail () {
|
||||
// log.error('No database connection, terminating.');
|
||||
// process.exit(1);
|
||||
// },
|
||||
// interval: 1000,
|
||||
// limit: 10
|
||||
// });
|
||||
//
|
||||
// @example
|
||||
// let sent = false;
|
||||
// tryer({
|
||||
// until: () => sent,
|
||||
// action: done => {
|
||||
// smtp.send(email, error => {
|
||||
// if (! error) {
|
||||
// sent = true;
|
||||
// }
|
||||
// done();
|
||||
// });
|
||||
// },
|
||||
// pass: next,
|
||||
// interval: -1000,
|
||||
// limit: -1
|
||||
// });
|
||||
function tryer (options) {
|
||||
options = normaliseOptions(options);
|
||||
|
||||
iterateWhen();
|
||||
|
||||
function iterateWhen () {
|
||||
if (preRecur()) {
|
||||
iterateUntil();
|
||||
}
|
||||
}
|
||||
|
||||
function preRecur () {
|
||||
return conditionallyRecur('when', iterateWhen);
|
||||
}
|
||||
|
||||
function conditionallyRecur (predicateKey, iterate) {
|
||||
if (! options[predicateKey]()) {
|
||||
incrementCount(options);
|
||||
|
||||
if (shouldFail(options)) {
|
||||
options.fail();
|
||||
} else {
|
||||
recur(iterate, postIncrementInterval(options));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function iterateUntil () {
|
||||
var result;
|
||||
|
||||
if (isActionSynchronous(options)) {
|
||||
result = options.action();
|
||||
|
||||
if (result && isFunction(result.then)) {
|
||||
return result.then(postRecur, postRecur);
|
||||
}
|
||||
|
||||
return postRecur();
|
||||
}
|
||||
|
||||
options.action(postRecur);
|
||||
}
|
||||
|
||||
function postRecur () {
|
||||
if (conditionallyRecur('until', iterateUntil)) {
|
||||
options.pass();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function normaliseOptions (options) {
|
||||
options = options || {};
|
||||
return {
|
||||
count: 0,
|
||||
when: normalisePredicate(options.when),
|
||||
until: normalisePredicate(options.until),
|
||||
action: normaliseFunction(options.action),
|
||||
fail: normaliseFunction(options.fail),
|
||||
pass: normaliseFunction(options.pass),
|
||||
interval: normaliseNumber(options.interval, -1000),
|
||||
limit: normaliseNumber(options.limit, -1)
|
||||
};
|
||||
}
|
||||
|
||||
function normalisePredicate (fn) {
|
||||
return normalise(fn, isFunction, yes);
|
||||
}
|
||||
|
||||
function isFunction (fn) {
|
||||
return typeof fn === 'function';
|
||||
}
|
||||
|
||||
function yes () {
|
||||
return true;
|
||||
}
|
||||
|
||||
function normaliseFunction (fn) {
|
||||
return normalise(fn, isFunction, nop);
|
||||
}
|
||||
|
||||
function nop () {
|
||||
}
|
||||
|
||||
function normalise (thing, predicate, defaultValue) {
|
||||
if (predicate(thing)) {
|
||||
return thing;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
function normaliseNumber (number, defaultNumber) {
|
||||
return normalise(number, isNumber, defaultNumber);
|
||||
}
|
||||
|
||||
function isNumber (number) {
|
||||
return typeof number === 'number' && number === number;
|
||||
}
|
||||
|
||||
function isActionSynchronous (options) {
|
||||
return options.action.length === 0;
|
||||
}
|
||||
|
||||
function incrementCount (options) {
|
||||
options.count += 1;
|
||||
}
|
||||
|
||||
function shouldFail (options) {
|
||||
return options.limit >= 0 && options.count >= options.limit;
|
||||
}
|
||||
|
||||
function postIncrementInterval (options) {
|
||||
var currentInterval = options.interval;
|
||||
|
||||
if (options.interval < 0) {
|
||||
options.interval *= 2;
|
||||
}
|
||||
|
||||
return currentInterval;
|
||||
}
|
||||
|
||||
function recur (fn, interval) {
|
||||
setTimeout(fn, Math.abs(interval));
|
||||
}
|
||||
}(this));
|
||||
|
||||
Reference in New Issue
Block a user