UNPKG

sproutcore

Version:

SproutCore buildtools: Node.js buildtools for SproutCore, written in SproutCore

193 lines (157 loc) 4.47 kB
/*globals BT, require*/ /* Purpose of this is that you can simply do: BT.NodeJSRequest.from('fs').perform('readFile','path/to/file').notify(this,this.hasChanged).start(); */ BT.AsyncWrapper = SC.Object.extend({ target: null, method: null, args: null, notifyTarget: null, notifyMethod: null, notifyArgs: null, isSync: false, returnValue: null, start: function () { var me = this; var t = this.target; var m = t[this.method]; var cb = function (err, result) { if (err) { var util = require('util'); BT.Logger.error("error found when executing BT.AsyncWrapper"); BT.Logger.error("method: " + me.method); BT.Logger.error("error: " + util.inspect(err)); } if (me.method === "exec") { BT.Logger.info("stdout: " + result); } SC.RunLoop.begin(); me.result.apply(me, arguments); SC.RunLoop.end(); }; var newArgs; var ret; if (this.isSync) { newArgs = this.args; try { ret = m.apply(t, newArgs); this.returnValue = ret; cb(null, ret); } catch (e) { cb(e, ret); } } else { newArgs = this.args ? this.args.concat(cb) : [cb]; this.returnValue = m.apply(t, newArgs); } return this; }, send: function () { // convenience method for people too much used to SC.Request this.start.apply(this, arguments); }, result: function () { var resultArgs = SC.A(arguments); var t = this.get('notifyTarget'); var m = this.get('notifyMethod'); var err = resultArgs[0]; var retArgs = resultArgs.slice(1); var ret = resultArgs[0] ? [{ isError: true, err: err }].concat(retArgs) : retArgs; var newargs = ret.concat(this.notifyArgs); if (SC.typeOf(m) === SC.T_FUNCTION) { m.apply(t, newargs); } else if (SC.typeOf(m) === SC.T_STRING) { t[m].apply(t, newargs); } if (this.shouldSchedule) BT.AsyncManager.ready(this); }, sync: function () { this.isSync = true; return this; }, notify: function (target, method) { this.notifyTarget = target; this.notifyMethod = method; this.notifyArgs = SC.A(arguments).slice(2); if (this.shouldSchedule) BT.AsyncManager.schedule(this); return this; }, perform: function (m) { this.method = m; this.args = SC.A(arguments).slice(1); return this; }, shouldSchedule: true, unschedule: function () { // very useful for watchers, can be called without managers supervision this.shouldSchedule = false; return this; } }); BT.AsyncWrapper.from = function (modname) { return BT.AsyncWrapper.create({ target: require(modname) }); }; BT.FSRequest = BT.AsyncWrapper.extend({ target: require('fs') }); BT.AsyncManager = SC.Object.create({ init: function () { this._queue = []; this._inFlight = []; if (require('os').platform() !== "win32") this._guessMaxOpen(); }, _guessMaxOpen: function () { var me = this; var exec = require('child_process').exec; exec('ulimit -n', function (err, stdout, stderr) { var m = 0, num = 0; if (err === null) { m = parseInt(stdout.trim(), 10); if (m > 0) { num = m / 2; me.maxOpen = (num >= 4) ? num: 4; BT.Logger.debug("number of max files: " + me.maxOpen); } } else { BT.Logger.error('BT.AsyncManager: Error calculating maximum of simultaneous open files'); } }); }, maxOpen: 128, _queue: null, _inFlight: null, perform: function (target, method) { var args = SC.A(arguments).slice(2); return BT.AsyncWrapper.create({ target: require(target), method: method, args: args }); }, schedule: function (req) { BT.Logger.debug("BT.AsyncManager#schedule: in flight %@, removing req: %@".fmt(this._inFlight.length, req.args)); this._queue.push(req); this.parseQueue(); }, ready: function (req) { BT.Logger.debug("BT.AsyncManager#ready: in flight %@, removing req: %@".fmt(this._inFlight.length, req.args)); this._inFlight.removeObject(req); this.parseQueue(); }, parseQueue: function () { var task; if (this._queue.length > 0) { if (this._inFlight.length < this.get('maxOpen')) { task = this._queue.shift(); this._inFlight.push(task); task.start(); } else this.invokeLater('parseQueue'); } } });