Last active
April 10, 2017 20:00
-
-
Save jayrbolton/f207a4c78f0dd1dc0dd08560f142485c to your computer and use it in GitHub Desktop.
callback helpers library sketch
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
// With asynchronous functions that take callbacks, it'd be easy to compose them if they were all curried | |
// This could create an interface very similar to Promises, but with plain, easy-to-use callback functions | |
const ch = require('callback-heaven') | |
const fs = require('fs') | |
// Contrived example function to open a json file, merge the data with an object, write the result back to the file, and read the file again | |
// (of course you could also just use writeFileSync and readFileSync, but this is just an example) | |
function mergeJson(path, obj, cb) { | |
fs.readFile(path, function(err, data) { | |
obj = merge(JSON.stringify(data), obj) | |
fs.writeFile(path, JSON.stringify(obj), function(err, data) { | |
fs.readFile(path, function(err, data) { | |
cb(data) | |
}) | |
} | |
}) | |
// Instead, if we curried the async functions, we could compose them and have a flatter structure | |
fs.writeFile = ch.curry(fs.writeFile) | |
fs.readFile = ch.curry(fs.readFile) | |
// We can curry the whole function to have three arguments: (path, obj, callback) | |
// This functions returns a composition of three other async calls | |
// ch.composeSync itself returns a function that takes a callback function | |
// (with compose, read bottom up -- ie the last function in the list is called first) | |
const mergeJson = ch.curry(3, (path, obj) => { | |
const mergeJ = json => merge(JSON.stringify(data), obj) | |
return ch.composeSync([ | |
fs.readFile(path) | |
, (err, data) => fs.writeFile(path, mergeJ(data)) | |
, fs.readFile(path) | |
]) | |
}) | |
// Instead of each subsequent async call getting nested, it's all flat like Promises | |
// Error handling could be added to `ch.compose` as well, similar to promises. | |
// A more complicated example: | |
// - simultaneously run three async functions a, b, c | |
// - when those are all done, run another async function d on the results | |
// - when that is done, run a final async function e | |
const abc = ch.composeAsync([a(1), b(2), c(3)]) | |
const final = ch.composeSync([e(4), d])(abc) | |
final(function(result) { /* result is the final result of a,b,c,d,e finishing */}) | |
// A couple other higher-order functions, like reduce and map, could be useful as well | |
// Apply the async function A to an array of data asynchronously, getting back a function that takes a callback with the full array of results when it's completed: | |
const a = ch.curry(2, (elem, cb) => elem) //do something asynchronous with the value | |
const arr = [1,2,3,4,5] | |
ch.map(a, arr, function (resultingArray) { | |
// resultingArray has all the values from arr with the `a` function applied to each asynchronously | |
}) | |
// Or you could reduce over an array of values to aggregate an accumulator asynchronously | |
const fetchUser = ch.curry(2, (id, cb) => fetch(`users/${id}`, cb)) | |
const appendUser = (id, users) => ch.compose([ | |
fetched => users.concat([fetched]) | |
, fetchUser(id) | |
]) | |
const userIds = [1,2,3,4] | |
ch.reduce( appendUser, [], userIds, function(users) { | |
// users is the final array of accumulated users | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment