JSFiddle - React, Tailwind, and code Playground

HTML

<script>var Attempt=function(){function c(d,e){return this instanceof c?(this.promise=a(),this.promiseGenerator=d,this.decisionGenerator=e||b,this.currentAttemptNumber=0,this.error=null,this._attempt(),this.promise):new c(d,e)}var a=function(){return{then:function(){throw new Error("Attempt.configure() before use Attempt")}}},b=function(){return!1};return c.configure=function(b){a=b},c.prototype={_attempt:function(){var a=this,b=a.promise;a.promiseGenerator(a.error,a.currentAttemptNumber).then(function(a){(b.fulfill||b.resolve).call(b,a)},function(b){a.error=b,a.currentAttemptNumber++;var c=a.createDecide(),d=!1;try{d=a.decisionGenerator(a.error,a.currentAttemptNumber,c)}catch(e){}"undefined"!=typeof d&&c(d)})},_decide:function(a){var b=this;return 1/0===a||"undefined"==typeof a||a===!1||null===a?(this.promise.reject(this.error),void 0):"function"==typeof a.then?(this._followPromise(a),void 0):(this.promise.notify([this.error,this.currentAttemptNumber]),setTimeout(function(){b._attempt()},a),void 0)},_followPromise:function(a){var b=this;a.then(function(a){b._decide(a)},function(){b._decide()})},createDecide:function(){var a=this,b=!1;return function(c){return b?void 0:(b=!0,a._decide.call(a,c))}}},c}();</script>
<pre></pre>

JavaScript

Attempt.configure(function () {
    return $.Deferred();
});

new Attempt(toGetJson, decideOnReject)
.pipe(resolve, reject, progress);

function toGetJson() {
   return $.getJSON('/json');
}

function decideOnReject(err, attemptNo) {
    if (err && err.status > 500) {
        return false;
    }
    
    // repeat request in N * 500 ms
    if (attemptNo <= 5) {
        return attemptNo * 500;
    }

    // To many attempts - network or server totally down, 
    // we should ask user what to do
    return askUserWhatTodo(attemptNo);
}

function askUserWhatTodo(attemptNo) {
    var dfd = $.Deferred();
    // setImmediate
    setTimeout(function () {
        var result = window.confirm('We had tried to send request ' + attemptNo + ' times. Continue?');
        var nextAttemptIn = attemptNo * 500;
        if (result) {
            dfd.resolve(nextAttemptIn);
        } else {
            dfd.reject();
        }
    }, 0);
    return dfd.promise();
}

function log(str) {
   $('<div>' + str + '</div>').appendTo('pre');
}

function resolve(data) {
    log(data);
}

function reject() {
    log('I tried several times... and fail :( ');
}

function progress(args) {
    log('Failed to load, retrying... ' + args[1]);
}