Skip to content

Instantly share code, notes, and snippets.

@TGOlson
Last active August 29, 2015 14:14
Show Gist options
  • Select an option

  • Save TGOlson/b726533677f1389f235a to your computer and use it in GitHub Desktop.

Select an option

Save TGOlson/b726533677f1389f235a to your computer and use it in GitHub Desktop.
Functional async helper strategies
var Q = require('q'),
_ = require('lodash');
/*
* Functional Helpers
*/
/**
* Compose two functions into one
* @param {Function} a
* @param {Function} b
* @return {Function} a(b())
*/
function compose(a, b) {
return function() {
return a(apply(b, arguments));
};
}
/**
* Compose two functions into one with additional arguments
* @param {Function} a
* @param {Function} b
* @param {Number} argCountA - number of additional arguments function 'a' will accept
* @return {Function} a(b(), additionalArgs)
*/
function composeWithArgs(a, b) {
var additionalArgs = arguments.length - 2;
return function() {
var argsB = spliceArgs.call(arguments, 0, arguments.length - additionalArgs),
resultB = apply(b, argsB);
return apply(_.partial(a, resultB), arguments);
};
}
var spliceArgs = Array.prototype.splice;
/**
* Invoke a function with an array of arguments
* @param {Function} fn
* @param {Array} args
* @return {*}
*/
function apply(fn, args) {
return fn.apply(null, args);
}
/*
* Core Logic Functions
*/
function resolveAll(collection) {
return Q.all(collection);
}
function map(collection, callback) {
return _.map(collection, callback);
}
function bindResolutionValue(promise, resolveTo) {
return promise.then(_.constant(resolveTo));
}
var mapAndResolveAll = compose(resolveAll, map);
// function mapAndResolveAll(collection, callback) {
// return Q.all(map(collection, callback));
// }
var mapAndResolveAllTo = composeWithArgs(bindResolutionValue, mapAndResolveAll, _);
// function mapAndResolveAllTo(collection, callback, resolveTo) {
// return bindResolutionValue(Q.all(map(collection, callback)), resolveTo);
// }
/*
* Test Setup
*/
function asynchFn(callback) {
return Q.Promise(function(resolve, reject, notify) {
setTimeout(function() {
resolve((callback || _.noop).call());
}, 500);
});
}
function loadEntity(entity, pattern) {
return entity.load(pattern);
}
var log = console.log.bind(console),
// fake entity
entity = {
load: function(pattern) {
return asynchFn(_.extend.bind(_, this, pattern));
}
},
// array of fake entities
arr = [
entity,
entity,
entity
],
// dummy load pattern
pattern = {
type: true
},
// dummy value for resolution bindings
dummyVal = {
dummyVal: true
},
loadEntityWithPattern = _.partialRight(loadEntity, pattern);
mapAndResolveAll(arr, loadEntityWithPattern).then(log);
// wait 500 ms
// => [ { load: [Function], name: true },
// { load: [Function], name: true },
// { load: [Function], name: true } ]
bindResolutionValue(asynchFn(), dummyVal).then(log);
// wait 500 ms
// => { dummyVal: true }
var promises = mapAndResolveAll(arr, loadEntityWithPattern);
bindResolutionValue(promises, dummyVal).then(log);
// wait 500 ms
// => { dummyVal: true }
mapAndResolveAllTo(arr, loadEntityWithPattern, dummyVal).then(log);
// wait 500 ms
// => { dummyVal: true }
// Last example using a traditional method:
// (no composition or reusable functions)
//
// var promises = _.map(arr, function(entity) {
// return entity.load(pattern);
// });
//
// Q.all(promises).then(function() {
// return dummyVal
// }).then(function(result) {
// console.log(result);
// });
//
// // wait 500 ms
// => { dummyVal: true }
@TGOlson
Copy link
Author

TGOlson commented Jan 31, 2015

Try using curry and partialRight to signal bindResolutionValue needs another argument. curry and partialRight could also be implemented in the composeWithArgs function. Example:

var mapAndResolveAllTo = _.curry(composeWithArgs(_.partialRight(bindResolutionValue, _), mapAndResolveAll));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment