Skip to content

Instantly share code, notes, and snippets.

@lxe
Created November 5, 2013 22:33
Show Gist options
  • Save lxe/7327556 to your computer and use it in GitHub Desktop.
Save lxe/7327556 to your computer and use it in GitHub Desktop.
var _ = require('lodash');
// Detect and fire all async functions in a plain JS object.
// Assume all functions take [callback(data)] as argument
//
// 1. Traverse the object:
// 2. If a function is found, replace with a AsyncResolver instance which:
// 2.1 - Fires the function
// 2.2 - Increments asyncsCreated
// 3. When each AsyncResolver callback fires, it does this:
// 3.1 - Increments asyncsResolved
// 3.2 - Assigns the result of the callback to own _result property
// 3.2 - Compares if asyncsCreated == asyncsResolved
// and performs the final transform (5)
// 4. For all non-function objects in the tree, assign it's original value
// 5. For all objects that only have _result property, assign the value
// of _result to the object itself.
// 6. Call 'callback' with transformed data as argument.
//
// TODO:
// - handle timeouts
// - handle callbacks that never fired
function resolveAsync(data, callback) {
var asyncsCreated = 0
, asyncsResolved = 0
function transform(result, val, key) {
if (val._result) {
// Flatten promise result
result[key] = val._result;
} else if (_.isFunction(val)) {
// Fire an async function
result[key] = new AsyncResolver(val);
} else if (_.isPlainObject(val)) {
// Continue transforming an object
result[key] = _.transform(val, transform);
} else {
// Leaf node
result[key] = val;
}
}
function AsyncResolver (fn) {
var self = this
, fired = false;
asyncsCreated ++;
fn(function(result) {
if (fired) {
throw new Error('Callback already fired');
}
fired = true;
self._result = result;
asyncsResolved ++;
if (asyncsResolved === asyncsCreated) {
// Detect and flatten all callback results
callback(_.transform(data, transform));
}
});
}
// Detect and fire all async functions
data = _.transform(data, transform);
// No async functions detected, return plain object
if (asyncsCreated === 0) callback(data);
}
// Test
resolveAsync({
foo : 'bar',
someAsyncProperty : function(done) {
setTimeout(function() {
done({
zoo : 'firedAfter2Seconds'
})
}, 2000);
}
}, function(data) {
console.log(data);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment