Created
November 5, 2013 22:33
-
-
Save lxe/7327556 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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