Weekend Fun 03: Retryable Task - js edition
In this article
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 `
How many times we want to retry`
How long should we wait between a retry and another.
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
A function to call if we failed (after trying the given number of times).
A function to call if we succeed (example: in my case this is where I will start listening to events).
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; })();