UNPKG

@testim/testim-cli

Version:

Command line interface for running Testing on you CI

108 lines (92 loc) 3.32 kB
var Promise = require('bluebird'); // Subclass of Error that can be thrown to indicate that retry should stop. // // If called with an instance of Error subclass, then the retry promise will be // rejected with the given error. // // Otherwise the cancel error object itself is propagated to the caller. // function StopError(err) { this.name = 'StopError'; if (err instanceof Error) { this.err = err } else { this.message = err || 'cancelled' } } StopError.prototype = Object.create(Error.prototype); retry.StopError = StopError; // Retry `func` until it succeeds. // // Waits `options.interval` milliseconds (default 1000) between attempts. // // Increases wait by a factor of `options.backoff` each interval, up to // a limit of `options.max_interval`. // // Keeps trying until `options.timeout` milliseconds have elapsed, // or `options.max_tries` have been attempted, whichever comes first. // // If neither is specified, then the default is to make 5 attempts. function retry(func, options) { options = options || {}; var interval = typeof options.interval === 'number' ? options.interval : 1000; var max_tries, giveup_time; if (typeof(options.max_tries) !== 'undefined') { max_tries = options.max_tries; } if (options.timeout) { giveup_time = new Date().getTime() + options.timeout; } if (!max_tries && !giveup_time) { max_tries = 5; } var tries = 0; var start = new Date().getTime(); function try_once() { var tryStart = new Date().getTime(); return Promise.try(function() { return func(); }) .catch(function(err) { if (err instanceof StopError) { if (err.err instanceof Error) { return Promise.reject(err.err); } else { return Promise.reject(err); } } ++tries; if (tries > 1) { interval = backoff(interval, options); } var now = new Date().getTime(); if ((max_tries && (tries === max_tries) || (giveup_time && (now + interval >= giveup_time)))) { var timeout = new Error('operation timed out after ' + (now - start) + ' ms, ' + tries + ' tries' + ' failure: ' + err.message); timeout.failure = err; timeout.code = 'ETIMEDOUT'; timeout.stack = err.stack; return Promise.reject(timeout); } else { var delay = interval - (now - tryStart); if (delay <= 0) { return try_once(); } else { return Promise.delay(delay).then(try_once); } } }); } return try_once(); } // Return the updated interval after applying the various backoff options function backoff(interval, options) { if (options.backoff) { interval = interval * options.backoff; } if (options.max_interval) { interval = Math.min(interval, options.max_interval); } return interval; } module.exports = retry;