Weekend Fun 03: Retryable Task - js edition

for the fun of it let us re-do .NET RetryableTask - https://blogs.msdn.com/b/bursts_of_random_architecture_thoughts/archive/2015/03/14/weekend-fun-02-retryable-tasks.aspx -  as JavaScript (for nodejs or modify it for your favorite JS platform). I admit this one is not "for fun" tomorrow I need to modify a nodejs app - will run on raspberry PI - which I have been working on. The app, upon starting, connects to a REST API to get some configuration (example: which event hub I am will connect to etc..). After which I can start listening to telemetry events and send them to Azure Event Hub. If the REST API is experiencing a transient error at the exact moment of my app starting-up the entire app will fail and device becomes a dud until a restart. so I needed to do this one

Here is how it will look like. create a task setting `

  1. How many times we want to retry`
  2. How long should we wait between a retry and another.
  3. A function to filter errors. so in my case, server errors are ok to retry against but authentication errors are not. for filtering errors in JS refer to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
  4. A function to call if we failed (after trying the given number of times).
  5. A function to call if we succeed (example: in my case this is where I will start listening to events).
  6. The function I want to try executing.
  1 2 3 4 5 6 7 8 91011121314151617181920212223242526
 var rtmodule = require('./RetryableTask.js');var totalRetry = 3;var retryIntervalSec = 1;var rt = new rtmodule.retrybleTask(totalRetry, retryIntervalSec);// or//var rt = rtmodule.retrybleTask(3, 1);rt.on("failif", function (err) {    console.log("failif failed with err: " + err);    return false;}).on("finalfail" , function (err, trycount) {    console.log("final fail: " + err + " " + trycount);}).on("done", function (TriedCound) {    console.log("sucess! try cound is:" + TriedCound);}).on("execute", function () {    var d = new Date();    if (d.getSeconds() % 2 == 0)        throw "RANDOM ERROR";});console.log("about to run");rt.run(); // let there be light!

 

the module code is below, enjoy!

   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 99100101102103104
 (function () {    function _retrybleTask(retryCount, delaySeconds) {                if (!(this instanceof _retrybleTask)) {             return new _retrybleTask(retryCount, delaySeconds);        }        this._retryCount = retryCount | 1;        this._currentRetry = 1;        this._delaySeconds = delaySeconds | 1;                // this is what the task will execute        this._execute = function () {             throw "this retryable is empty! with nothing to execute";        };                // our fail filter        this._failIf = function (err) {            return true // default we fail with every error        };                // if we successed, ultimatly        this._done = function (tryCount) {            // this what we will execute if we managed to successfully execute         };                // if we failed, ultimatly        this._finalfail = function ( err, tryCount) {            console.log("Retryable Task Failed: trycount{" + tryCount + "}" + " err was {" + err + "}");         };        return this;    }    _retrybleTask.prototype.on = function(action, callback)    {                var events = ["failif", "done", "execute", "finalfail"];                if (typeof callback != "function")            throw "callback is not a function";                if (-1 == events.indexOf(action))            throw "unsupported action";        if ("failif" === action) {             this._failIf = callback;            return this;        }        if ("done" === action) {             this._done = callback;            return this;        }        if ("execute" === action) {            this._execute = callback;            return this;        }        if ("finalfail" === action) {            this._finalfail = callback;            return this;        }            }    _retrybleTask.prototype.run = function () {        var self = this;        var bSucess = false;        try {                        this._execute();            bSucess = true;        }        catch (err) {            if (this._failIf(err)) {                this._finalfail(this._currentRetry, err);                return;            }            if (this._currentRetry === this._retryCount) {                 this._finalfail( err, this._currentRetry);                return;            }            // set next execute cycle            this._currentRetry++;            setTimeout(function () { self.run() }, this._delaySeconds * 1000)            }        finally {            if (bSucess) {                this._done(this._currentRetry );            }                    }           }        module.exports.retrybleTask = _retrybleTask;     })();