UNPKG

laps

Version:

Simple flow-control lib for Node.js: combine sync/async steps, organize your spaghetti-code

219 lines (204 loc) 4.67 kB
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;