Last active
August 29, 2015 14:14
-
-
Save TGOlson/b726533677f1389f235a to your computer and use it in GitHub Desktop.
Functional async helper strategies
This file contains hidden or 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 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 } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Try using
curryandpartialRightto signalbindResolutionValueneeds another argument.curryandpartialRightcould also be implemented in thecomposeWithArgsfunction. Example: