Skip to content

Instantly share code, notes, and snippets.

@jayrbolton
Last active April 10, 2017 20:00
Show Gist options
  • Save jayrbolton/f207a4c78f0dd1dc0dd08560f142485c to your computer and use it in GitHub Desktop.
Save jayrbolton/f207a4c78f0dd1dc0dd08560f142485c to your computer and use it in GitHub Desktop.
callback helpers library sketch
// 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