Skip to content

Instantly share code, notes, and snippets.

@thomasboyt
Last active August 29, 2015 13:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thomasboyt/9286079 to your computer and use it in GitHub Desktop.
Save thomasboyt/9286079 to your computer and use it in GitHub Desktop.
/**
* A restartable promise chain using q (tho it could be adapted for other libs).
*
* Usage:
* var chain = new RestartableChain([firstFn, secondFn, thirdFn...]);
*
* chain.execute() - run chain, returning a promise that is resolved if all promises
* in the chain resolve, otherwise rejects at first rejection.
*
* If the chain has been previously run & errored, this method will restart from the
* function that rejected.
* i.e, if secondFn rejects, then rerunning chain.execute() will run secondFn and thirdFn
*/
var q = require('q');
var RestartableChain = function(fns) {
this.fns = fns;
this.fnIndex = 0;
};
RestartableChain.prototype._next = function() {
var idx = this.fnIndex;
if ( idx >= this.fns.length ) {
this.dfd.resolve(this.lastPayload);
return;
}
this.fns[idx](this.lastPayload).then(function(payload) {
this.fnIndex += 1;
this.lastPayload = payload;
this._next();
}.bind(this), function(err) {
this.dfd.reject(err);
}.bind(this));
return this.dfd.promise;
};
RestartableChain.prototype.execute = function() {
// reset the promise
this.dfd = q.defer();
return this._next();
};
module.exports = RestartableChain;
var assert = require('assert');
var q = require('q');
var RestartableChain = require('../index');
describe('Restartable chain', function() {
/*
* Stubs
*/
var oneCalled, twoCalled, errCalled, didError;
var one = function() {
oneCalled += 1;
var dfd = q.defer();
dfd.resolve();
return dfd.promise;
};
var two = function() {
twoCalled += 1;
var dfd = q.defer();
dfd.resolve('success');
return dfd.promise;
};
var err = function() {
errCalled += 1;
var dfd = q.defer();
if ( !didError ) {
dfd.reject('error');
didError = true;
} else {
dfd.resolve('success');
}
return dfd.promise;
};
beforeEach(function() {
oneCalled = 0;
twoCalled = 0;
errCalled = 0;
didError = false;
});
it('calls each fn in the chain', function(done) {
var chain = new RestartableChain([one, two]);
chain.execute().done(function(payload) {
assert.equal(chain.fnIndex, 2);
assert.equal(payload, 'success');
done();
});
});
it('can resume from a rejected handler', function(done) {
var chain = new RestartableChain([one, err]);
var makeRequest = function() {
chain.execute().done(function(payload) {
assert.equal(oneCalled, 1);
assert.equal(errCalled, 2);
assert.equal(payload, 'success');
assert.equal(didError, true);
done();
}, function(err) {
assert.equal(err, 'error');
makeRequest();
});
};
makeRequest();
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment