UNPKG

ppl

Version:

Promise implementation with advanced flow control capabilities.

558 lines (485 loc) 14.1 kB
var AllSegment, FuncSegment, JoinSegment, Pipeline, RaceSegment, Segment, SplitSegment, State, as_promise, head, ref, tail, extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, hasProp = {}.hasOwnProperty; ref = require('./utils'), head = ref.head, tail = ref.tail; as_promise = function(thing, data, context) { var prom; if (context == null) { context = {}; } if ((thing != null ? thing.then : void 0) != null) { return thing; } else if (typeof thing === 'function') { prom = new FuncSegment(thing, void 0); prom._context = context; prom._proceed_fulfill(data); return prom; } else { return Pipeline.resolve(thing); } }; State = { Pending: 'pending', Fulfilled: 'fulfilled', Rejected: 'rejected' }; Segment = (function() { function Segment() { this._context = {}; this.state = State.Pending; } Segment.prototype.context = function(_context) { this._context = _context; return this; }; Segment.prototype.pipe = function(func) { if (Array.isArray(func)) { if (func.length === 0) { return this._pass(); } return this.pipe(head(func)).pipe(tail(func)); } else if (func === void 0) { return this._pass(); } else { return this._pipe(func); } }; Segment.prototype.then = function(fulfill, reject) { return this._pipe(fulfill, reject); }; Segment.prototype.done = function(fulfill, reject) { return this.then(fulfill, reject); }; Segment.prototype["catch"] = function(reject) { return this._pipe(void 0, reject); }; Segment.prototype.split = function(map_func) { if (map_func != null) { return this.pipe(map_func).split(); } return this.extend(new SplitSegment()); }; Segment.prototype.map = function(func) { if (func === void 0) { return this._pass(); } return this.split().pipe(func).join(); }; Segment.prototype.all = function(items) { return this.extend(new AllSegment(items)); }; Segment.prototype.race = function(items) { return this.extend(new RaceSegment(items)); }; Segment.prototype.join = function(join_func) { if (this._split_head != null) { return this._split_head.join(join_func); } return this._pipe(join_func); }; Segment.prototype.extend = function(segment) { return this._extend(segment); }; Segment.prototype._extend = function(segment) { this.next_segment = segment; segment.prev_segment = this; segment._context = this._context; segment._split_head = this._split_head; switch (this.state) { case State.Fulfilled: segment._proceed_fulfill(this._result); break; case State.Rejected: segment._proceed_reject(this._error); } return segment; }; Segment.prototype.toString = function() { return this.constructor.name + " (state: " + this.state + ", result: " + this._result + ", error: " + this._error + ")"; }; Segment.prototype._proceed_fulfill = function(data) { return this._fulfill(data); }; Segment.prototype._proceed_reject = function(error) { return this._reject(error); }; Segment.prototype._fulfill = function(_result) { var ref1; this._result = _result; switch (this.state) { case State.Rejected: throw 'Pipeline segment cannot be fulfilled, already rejected'; break; case State.Fulfilled: throw 'Pipeline segment cannot be fulfilled, already fulfilled'; } this.state = State.Fulfilled; return (ref1 = this.next_segment) != null ? ref1._proceed_fulfill(this._result) : void 0; }; Segment.prototype._reject = function(_error) { var ref1; this._error = _error; if (this.state === State.Rejected) { throw 'Pipeline segment already rejected!'; } this.state = State.Rejected; return (ref1 = this.next_segment) != null ? ref1._proceed_reject(this._error) : void 0; }; Segment.prototype._pipe = function(fulfill, reject) { return this.extend(new FuncSegment(fulfill, reject)); }; Segment.prototype._pass = function() { return this.extend(new Segment()); }; Segment.prototype._clone = function() { var clone; clone = new this.constructor(); clone._context = this._context; if (this.next_segment != null) { clone.extend(this.next_segment._clone()); } return clone; }; Segment.prototype._first = function() { var current, prev; current = this; while (current != null) { prev = current.prev_segment; if (prev == null) { return current; } current = prev; } return this; }; Segment.prototype._last = function() { var current, next; current = this; while (current != null) { next = current.next_segment; if (next == null) { return current; } current = next; } return this; }; Segment.prototype._dump_pipeline = function() { var current, out; out = "Pipeline\n"; current = this._first(); while (current != null) { if (current === this) { out += "|* " + current + "\n"; } else { out += "| " + current + "\n"; } current = current.next_segment; } return out; }; return Segment; })(); FuncSegment = (function(superClass) { extend(FuncSegment, superClass); function FuncSegment(fulfill_func, reject_func) { this.fulfill_func = fulfill_func; this.reject_func = reject_func; FuncSegment.__super__.constructor.call(this); } FuncSegment.prototype._proceed_fulfill = function(data) { var err, result; if (this.fulfill_func == null) { return this._fulfill(data); } result = void 0; try { result = this.fulfill_func.apply(this._context, [data]); } catch (error1) { err = error1; return this._reject(err); } if ((result != null ? result.then : void 0) != null) { return result.then((function(_this) { return function(data) { return _this._fulfill(data); }; })(this))["catch"]((function(_this) { return function(error) { return _this._reject(error); }; })(this)); } else { return this._fulfill(result); } }; FuncSegment.prototype._proceed_reject = function(error) { var err, result; if (this.reject_func == null) { return this._reject(error); } try { result = this.reject_func.apply(this._context, [error]); } catch (error1) { err = error1; return this._reject(err); } if ((result != null ? result.then : void 0) != null) { return result.then((function(_this) { return function(data) { return _this._fulfill(data); }; })(this))["catch"]((function(_this) { return function(error) { return _this._reject(error); }; })(this)); } else { return this._reject(result); } }; FuncSegment.prototype._clone = function() { var clone; clone = FuncSegment.__super__._clone.call(this); clone.fulfill_func = this.fulfill_func; clone.reject_func = this.reject_func; return clone; }; return FuncSegment; })(Segment); SplitSegment = (function(superClass) { extend(SplitSegment, superClass); function SplitSegment() { SplitSegment.__super__.constructor.call(this); this.child_pipes = []; this.joined = false; } SplitSegment.prototype.extend = function(segment) { this.template = segment; segment._context = this._context; segment._split_head = this; return segment; }; SplitSegment.prototype._proceed_fulfill = function(incoming) { this.incoming = incoming; if (!Array.isArray(this.incoming)) { throw 'Can only split on Array context!'; } if (this.joined) { return this._process_children(); } }; SplitSegment.prototype._process_children = function() { var i, item, len, ref1, segment; ref1 = this.incoming; for (i = 0, len = ref1.length; i < len; i++) { item = ref1[i]; segment = Pipeline.source(item).context(this._context); if (this.template != null) { segment = segment.extend(this.template._clone()); } this.child_pipes.push(segment._last()); } return this._fulfill(this.incoming); }; SplitSegment.prototype.join = function(join_func) { var segment; segment = this._extend(new JoinSegment(this)); if (join_func != null) { segment = segment.pipe(join_func); } this.joined = true; if (this.incoming != null) { this._process_children(); } return segment; }; SplitSegment.prototype._clone = function() { var clone, join; clone = new SplitSegment(); clone._context = this._context; if (this.template != null) { clone.template = this.template._clone(); } if (this.next_segment && this.next_segment.constructor.name === 'JoinSegment') { join = this.next_segment._clone(); clone._extend(join); clone.joined = true; join.split_segment = clone; } return clone; }; return SplitSegment; })(Segment); JoinSegment = (function(superClass) { extend(JoinSegment, superClass); function JoinSegment(split_segment) { this.split_segment = split_segment; JoinSegment.__super__.constructor.call(this); } JoinSegment.prototype._proceed_fulfill = function(data) { var pipes, process, results; results = []; pipes = this.split_segment.child_pipes; process = (function(_this) { return function() { var pipe; pipe = pipes.shift(); if (pipe == null) { return _this._fulfill(results); } return pipe.then(function(result) { results.push(result); return process(); })["catch"](function(err) { return _this._reject(err); }); }; })(this); return process(); }; return JoinSegment; })(Segment); AllSegment = (function(superClass) { extend(AllSegment, superClass); function AllSegment(items1) { this.items = items1; AllSegment.__super__.constructor.call(this); } AllSegment.prototype._proceed_fulfill = function(data) { var item, process, promises, results; results = []; promises = (function() { var i, len, ref1, results1; ref1 = this.items; results1 = []; for (i = 0, len = ref1.length; i < len; i++) { item = ref1[i]; results1.push(as_promise(item, data, this._context)); } return results1; }).call(this); process = (function(_this) { return function() { var promise; promise = promises.shift(); if (promise == null) { return _this._fulfill(results); } promise.then(function(result) { results.push(result); return process(); }); return promise["catch"](function(err) { return _this._reject(err); }); }; })(this); return process(); }; AllSegment.prototype._clone = function() { var clone; clone = AllSegment.__super__._clone.call(this); clone.items = this.item; return clone; }; return AllSegment; })(Segment); RaceSegment = (function(superClass) { extend(RaceSegment, superClass); function RaceSegment(items1) { this.items = items1; RaceSegment.__super__.constructor.call(this); } RaceSegment.prototype._proceed_fulfill = function(data) { var complete, i, item, len, promise, promises, results, results1; results = []; promises = (function() { var i, len, ref1, results1; ref1 = this.items; results1 = []; for (i = 0, len = ref1.length; i < len; i++) { item = ref1[i]; results1.push(as_promise(item, data, this._context)); } return results1; }).call(this); if (!(promises != null ? promises.length : void 0)) { return this._fulfill(void 0); } complete = false; results1 = []; for (i = 0, len = promises.length; i < len; i++) { promise = promises[i]; results1.push(promise.then((function(_this) { return function(result) { if (complete) { return; } complete = true; return _this._fulfill(result); }; })(this))["catch"]((function(_this) { return function(err) { if (complete) { return; } complete = true; return _this._reject(err); }; })(this))); } return results1; }; RaceSegment.prototype._clone = function() { var clone; clone = RaceSegment.__super__._clone.call(this); clone.items = this.item; return clone; }; return RaceSegment; })(Segment); Pipeline = (function(superClass) { extend(Pipeline, superClass); function Pipeline(callback) { Pipeline.__super__.constructor.call(this); callback((function(_this) { return function(data) { return _this._fulfill(data); }; })(this), (function(_this) { return function(err) { return _this._reject(err); }; })(this)); } Pipeline.source = function(data) { var segment; segment = new Segment(); segment._fulfill(data); return segment; }; Pipeline.resolve = function(data) { return this.source(data); }; Pipeline.reject = function(error) { var segment; segment = new Segment(); segment._reject(error); return segment; }; Pipeline.all = function(items) { var segment; segment = new AllSegment(items); segment._proceed_fulfill(); return segment; }; Pipeline.race = function(items) { var segment; segment = new RaceSegment(items); segment._proceed_fulfill(); return segment; }; return Pipeline; })(Segment); module.exports = Pipeline;