exports.funnel = function(max) { max = max == null ? -1 : max; if (max === 0) max = funnel.defaultSize; if (typeof max !== "number") throw new Error("bad max number: " + max); var queue = [], active = 0, closed = false; var funCb = function(callback, fn) { if (callback == null) return future(funCb, arguments, 0); //console.log("FUNNEL: active=" + active + ", queued=" + queue.length); if (max < 0 || max == Infinity) return galaxy.unstar(fn,0)( callback); queue.push({ fn: fn, cb: callback }); function _doOne() { var current = queue.splice(0, 1)[0]; if (!current.cb) return current.fn(); active++; galaxy.unstar(current.fn,0)( function(err, result) { active--; if (!closed) { current.cb(err, result); while (active < max && queue.length > 0) _doOne(); } }); } while (active < max && queue.length > 0) _doOne(); }; var fun = galaxy.star(funCb, 0); fun.close = function() { queue = [], closed = true; }; return fun; };
(function(exports) { "use strict"; var globals = require('streamline/lib/globals'); var dir = 'streamline/lib/' + globals.runtime; var builtins = require(dir + '/builtins'); /// !nodoc /// Obsolete API /// /// This API is obsolete. Use `array.forEach_`, `array.map_`, ... instead. /// /// * `flows.each(_, array, fn, [thisObj])` /// applies `fn` sequentially to the elements of `array`. /// `fn` is called as `fn(_, elt, i)`. exports.each = function*(_, array, fn, thisObj) { return (array && array.length) ? (yield array.forEach_(_, fn, thisObj)) : undefined; }; /// * `result = flows.map(_, array, fn, [thisObj])` /// transforms `array` by applying `fn` to each element in turn. /// `fn` is called as `fn(_, elt, i)`. exports.map = function*(_, array, fn, thisObj) { return array ? (yield array.map_(_, fn, thisObj)) : array; }; /// * `result = flows.filter(_, array, fn, [thisObj])` /// generates a new array that only contains the elements that satisfy the `fn` predicate. /// `fn` is called as `fn(_, elt)`. exports.filter = function*(_, array, fn, thisObj) { return array ? (yield array.filter_(_, fn, thisObj)) : array; }; /// * `bool = flows.every(_, array, fn, [thisObj])` /// returns true if `fn` is true on every element (if `array` is empty too). /// `fn` is called as `fn(_, elt)`. exports.every = function*(_, array, fn, thisObj) { return array ? (yield array.every_(_, fn, thisObj)) : undefined; }; /// * `bool = flows.some(_, array, fn, [thisObj])` /// returns true if `fn` is true for at least one element. /// `fn` is called as `fn(_, elt)`. exports.some = function*(_, array, fn, thisObj) { return array ? (yield array.some_(_, fn, thisObj)) : undefined; }; /// * `result = flows.reduce(_, array, fn, val, [thisObj])` /// reduces by applying `fn` to each element. /// `fn` is called as `val = fn(_, val, elt, i, array)`. exports.reduce = function*(_, array, fn, v, thisObj) { return array ? (yield array.reduce_(_, fn, v, thisObj)) : v; }; /// * `result = flows.reduceRight(_, array, fn, val, [thisObj])` /// reduces from end to start by applying `fn` to each element. /// `fn` is called as `val = fn(_, val, elt, i, array)`. exports.reduceRight = function*(_, array, fn, v, thisObj) { return array ? (yield array.reduceRight_(_, fn, v, thisObj)) : v; }; /// * `array = flows.sort(_, array, compare, [beg], [end])` /// sorts the array. /// `compare` is called as `cmp = compare(_, elt1, elt2)` /// /// Note: this function _changes_ the original array (and returns it) exports.sort = function*(_, array, compare, beg, end) { return array ? (yield array.sort_(_, compare, beg, end)) : array; }; /// /// ## Object utility (obsolete) /// /// This API is obsolete. Use `Object.keys(obj).forEach_` instead. /// /// * `flows.eachKey(_, obj, fn)` /// calls `fn(_, key, obj[key])` for every `key` in `obj`. exports.eachKey = function*(_, obj, fn, thisObj) { return (yield (obj ? Object.keys(obj) : []).forEach_(_, function*(_, elt) { (yield fn.call(thisObj, _, elt, obj[elt])); })); }; // deprecated -- don't document exports.spray = function(fns, max) { return new function() { var funnel = exports.funnel(max); this.collect = function*(_, count, trim) { return (yield galaxy.invoke((function(callback) { if (typeof(callback) != "function") throw new Error("invalid call to collect: no callback"); var results = trim ? [] : new Array(fns.length); count = count < 0 ? fns.length : Math.min(count, fns.length); if (count == 0) return callback(null, results); var collected = 0; for (var i = 0; i < fns.length; i++) { (function(i) { galaxy.unstar(funnel,0)( function(err, result) { if (err) return callback(err); if (trim) results.push(result); else results[i] = result; if (++collected == count) return callback(null, results); }, fns[i]); })(i); } }), "call", [this, _], 1)); }; this.collectOne = function*(_) { var result = (yield this.collect(_, 1, true)); return result && result[0]; }; this.collectAll = function*(_) { return (yield this.collect(_, -1, false)); }; }; }; /// * `fun = flows.funnel(max)` /// limits the number of concurrent executions of a given code block. /// /// The `funnel` function is typically used with the following pattern: /// /// ``` javascript /// // somewhere /// var myFunnel = flows.funnel(10); // create a funnel that only allows 10 concurrent executions. /// /// // elsewhere /// myFunnel(_, function(_) { /* code with at most 10 concurrent executions */ }); /// ``` /// /// The `diskUsage2.js` example demonstrates how these calls can be combined to control concurrent execution. /// /// The `funnel` function can also be used to implement critical sections. Just set funnel's `max` parameter to 1. /// /// If `max` is set to 0, a default number of parallel executions is allowed. /// This default number can be read and set via `flows.funnel.defaultSize`. /// If `max` is negative, the funnel does not limit the level of parallelism. /// /// The funnel can be closed with `fun.close()`. /// When a funnel is closed, the operations that are still in the funnel will continue but their callbacks /// won't be called, and no other operation will enter the funnel. exports.funnel = builtins.funnel; /// /// * `results = flows.collect(_, futures)` /// collects the results of an array of futures exports.collect = function*(_, futures) { return futures && (yield futures.map_(_, function*(_, future) { return (yield future(_)); })); }; // Obsolete API - use require('streamline/lib/globals').context instead var globals = require("streamline/lib/globals"); exports.setContext = function(ctx) { var old = globals.context; globals.context = ctx; return old; }; exports.getContext = function() { return globals.context; }; /// /// * `flows.nextTick(_)` /// `nextTick` function for both browser and server. /// Aliased to `process.nextTick` on the server side. var nextTick = typeof process === "object" && typeof process.nextTick === "function" ? process.nextTick : function(cb) { cb(); }; exports.nextTick = function*(_) { (yield galaxy.invoke(null, nextTick, [_], 0)); }; exports.setTimeout = function(fn, millis) { return setTimeout(function() { galaxy.spin.call(this, fn(false), 0); }, millis); }; exports.setInterval = function(fn, millis) { return setInterval(function() { galaxy.spin.call(this, fn(false), 0); }, millis); }; exports.setImmediate = function*(_, fn, thisObj) { (yield galaxy.invoke(null, (function(cb) { setImmediate(function() { galaxy.unstar(fn.bind(thisObj),0)( cb); }); }), [_], 0)); }; exports.sleep = function*(_, millis) { return (yield galaxy.invoke(null, setTimeout, [_, millis], 0)); }; exports.eventHandler = function(fn) { return function() { var that = this; var args = Array.prototype.slice(arguments, 0); return galaxy.unstar((function*(_) { return (yield fn.apply_(_, that, args, 0)); }),0)( function(err) { if (err) throw err; }); }; }; // Obsolete. Use `fn.apply_` instead. exports.apply = function* apply(_, fn, thisObj, args, index) { return (yield fn.apply_(_, thisObj, args, index)); }; exports.callWithTimeout = galaxy.star(function(cb, fn, millis) { var tid = setTimeout(function() { if (cb) { var ex = new Error("timeout"); ex.code = "ETIMEOUT"; ex.errno = "ETIMEOUT"; cb(ex); cb = null; } }, millis); galaxy.unstar(fn,0)( function(err, result) { if (cb) { clearTimeout(tid); cb(err, result); cb = null; } }); }, 0); })(typeof exports !== 'undefined' ? exports : (Streamline.flows = Streamline.flows || {}));