laps
Version:
Simple flow-control lib for Node.js: combine sync/async steps, organize your spaghetti-code
219 lines (204 loc) • 4.67 kB
JavaScript
var util = require('util');
var EventEmitter = require('events').EventEmitter;
function Laps() {
Laps.super_.call(this);
this._handlers = {
lap: [],
cmd: {}
};
this._callCount = 0;
this._cancelled = false;
this._runCount = 0;
this._cbCount = 0;
this._lap = 0;
this._err = null;
}
util.inherits(Laps, EventEmitter);
Laps.prototype.lap = function(handler) {
if (!arguments.length)
return this._lap + 1;
this._handlers.lap.push(handler);
return this;
};
Laps.prototype.cmd = function(name, handler) {
this._handlers.cmd[name] = handler;
return this;
};
Laps.prototype.run = function() {
if (++this._runCount > 1)
return this;
this._cancelled = false;
return this._run();
};
Laps.prototype.cancel = function() {
this._cancelled = true;
};
Laps.prototype.err = function(error) {
if (!arguments.length)
return this._err;
this._err = error;
if (this._err)
this.emit('error', this._err);
};
Laps.prototype.exec = function() {
var self = this;
++self._cbCount;
var params = arguments;
self._call(self._handlers.cmd[params[0]], params);
self._end();
};
Laps.prototype.cb = function() {
var self = this;
++self._cbCount;
var params = arguments;
return function() {
var args = arguments;
self._call(self._handlers.cmd[params[0]], params, args);
self._end();
};
};
/*
* Use these wrappers (cb1(), cb2(), ...) when callback arity is important (for example, for Express & Connect)
*/
Laps.prototype.cb1 = function() {
var self = this;
++self._cbCount;
var params = arguments;
return function(arg1) {
var args = arguments;
self._call(self._handlers.cmd[params[0]], params, args);
self._end();
};
};
Laps.prototype.cb2 = function() {
var self = this;
++self._cbCount;
var params = arguments;
return function(arg1, arg2) {
var args = arguments;
self._call(self._handlers.cmd[params[0]], params, args);
self._end();
};
};
Laps.prototype.cb3 = function() {
var self = this;
++self._cbCount;
var params = arguments;
return function(arg1, arg2, arg3) {
var args = arguments;
self._call(self._handlers.cmd[params[0]], params, args);
self._end();
};
};
Laps.prototype.cb4 = function() {
var self = this;
++self._cbCount;
var params = arguments;
return function(arg1, arg2, arg3, arg4) {
var args = arguments;
self._call(self._handlers.cmd[params[0]], params, args);
self._end();
};
};
Laps.prototype.cb5 = function() {
var self = this;
++self._cbCount;
var params = arguments;
return function(arg1, arg2, arg3, arg4, arg5) {
var args = arguments;
self._call(self._handlers.cmd[params[0]], params, args);
self._end();
};
};
/*
* Internals
*/
Laps.prototype._call = function(handler, params, args) {
var self = this;
if (!handler)
return;
try {
++self._callCount;
if ((!params || params.length <= 1) && (!args || args.length < 1)) {
handler.call(self);
return;
}
var getVal = function (index) {
var pIndex = index + 1;
if (pIndex < params.length) {
return params[pIndex];
} else if (args) {
pIndex -= params.length;
if (pIndex < args.length)
return args[pIndex];
else
return null;
} else {
return null;
}
};
switch (params.length + ((args) ? (args.length) : (0))) {
case 2:
handler.call(self, getVal(0));
break;
case 3:
handler.call(self, getVal(0), getVal(1));
break;
case 4:
handler.call(self, getVal(0), getVal(1), getVal(2));
break;
case 5:
handler.call(self, getVal(0), getVal(1), getVal(2), getVal(3));
break;
case 6:
handler.call(self, getVal(0), getVal(1), getVal(2), getVal(3), getVal(4));
break;
default:
var params = Array.prototype.slice.call(params, 1);
params = params.concat(Array.prototype.slice.call(args));
handler.apply(self, params);
break;
}
} catch(err) {
self.err(err);
} finally {
--self._callCount;
}
};
Laps.prototype._run = function() {
if (this._runCount <= 0)
return;
this._lap = 0;
this._cbCount = 0;
this._err = null;
if (this._handlers.lap.length > 0) {
this.emit('start');
this.emit('begin');
this._call(this._handlers.lap[this._lap]);
this._end(true);
}
return this;
};
Laps.prototype._end = function(check) {
if (!check)
--this._cbCount;
if (this._cbCount <= 0 && this._callCount <= 0) {
this.emit('end');
if (this._cancelled) {
this.emit('finish');
this._runCount = 0;
return;
}
++this._lap;
if (this._lap < this._handlers.lap.length) {
this.emit('begin');
this._call(this._handlers.lap[this._lap]);
this._end(true);
} else {
this.emit('finish');
--this._runCount;
this._run();
}
}
};
exports = module.exports = Laps;