/// provides the async helper functionality function Waiter() { if (!(this instanceof Waiter)) { return new Waiter(); } var self = this; // found values self.values = {}; // callback when done self.callback = null; self.resolved = false; self.count = 0; }; Waiter.prototype.wait = function() { var self = this; ++self.count; }; // resolve the promise Waiter.prototype.resolve = function(name, val) { var self = this; self.values[name] = val; // done with all items if (--self.count === 0) { self.resolved = true; // we may not have a done callback yet if (self.callback) { self.callback(self.values); } } }; // sets the done callback for the waiter // notifies when the promise is complete Waiter.prototype.done = function(fn) { var self = this; self.callback = fn; if (self.resolved) { fn(self.values); } }; var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_'; var gen_id = function() { var res = ''; for (var i=0 ; i<8 ; ++i) { res += alphabet[Math.floor(Math.random() * alphabet.length)]; } return res; }; module.exports = function() { // baton which contains the current // set of deferreds var waiter; var obj = Object.create(null); obj.done = function done(fn) { // no async things called if (!waiter) { return fn({}); } waiter.done(fn); // clear the waiter for the next template waiter = undefined; }; obj.resolve = function resolve(fn, args) { // we want to do async things, need a waiter for that if (!waiter) { waiter = new Waiter(); } var id = '__' + gen_id() + '__'; var cur_waiter = waiter; waiter.wait(); args = [].slice.call(args); args.push(function(res) { cur_waiter.resolve(id, res); }) fn.apply(null, args); // return the id placeholder // this will be replaced later return id; }; return obj; };