Created
December 27, 2011 17:56
-
-
Save creationix/1524578 to your computer and use it in GitHub Desktop.
Request for Comments on new API for Step
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
module.exports = TwoStep; | |
var slice = Array.prototype.slice; | |
function Group(callback) { | |
this.args = [null]; | |
this.left = 0; | |
this.callback = callback; | |
this.isDone = false; | |
} | |
Group.prototype.done = function done() { | |
if (this.isDone) return; | |
this.isDone = true; | |
this.callback.apply(null, this.args); | |
}; | |
Group.prototype.error = function error(err) { | |
if (this.isDone) return; | |
this.isDone = true; | |
var callback = this.callback; | |
callback(err); | |
}; | |
// Simple utility for passing a sync value to the next step. | |
Group.prototype.pass = function pass() { | |
var values = slice.call(arguments); | |
for (var i = 0, l = values.length; i < l; i++) { | |
this.args.push(values[i]); | |
} | |
}; | |
// Register a slot in the next step and return a callback | |
Group.prototype.slot = function slot() { | |
var group = this; | |
var index = group.args.length; | |
group.args.length++; | |
group.left++; | |
return function (err, data) { | |
if (err) return group.error(err); | |
group.args[index] = data; | |
if (--group.left === 0) group.done(); | |
}; | |
} | |
// Creates a nested group where several callbacks go into a single array. | |
Group.prototype.makeGroup = function makeGroup() { | |
var group = this; | |
var index = this.args.length; | |
this.args.length++; | |
group.left++; | |
return new Group(function (err) { | |
if (err) return group.error(err); | |
var data = slice.call(arguments, 1); | |
group.args[index] = data; | |
if (--group.left === 0) group.done(); | |
}); | |
}; | |
// Expose just for fun and extensibility | |
TwoStep.Group = Group; | |
// Stepper function | |
function exec(steps, args, callback) { | |
var pos = 0; | |
next.apply(null, args); | |
function next() { | |
var step = steps[pos++]; | |
if (!step) { | |
callback && callback.apply(null, arguments); | |
return; | |
} | |
var group = new Group(next); | |
step.apply(group, arguments); | |
if (group.left === 0) group.done(); | |
} | |
} | |
// Execute steps immedietly | |
function TwoStep() { | |
exec(slice.call(arguments), []); | |
} | |
// Create a composite function with steps built-in | |
TwoStep.fn = function () { | |
var steps = slice.call(arguments); | |
return function () { | |
var args = slice.call(arguments); | |
var callback = args.pop(); | |
exec(steps, args, callback); | |
}; | |
} |
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 TwoStep = require('./twostep'); | |
var FS = require('fs'); | |
var Path = require('path'); | |
// Create a composite function using TwoStep.fn | |
var statdir = TwoStep.fn( | |
function (directory) { | |
this.pass(directory); | |
FS.readdir(directory, this.slot()); | |
}, | |
function (err, directory, fileNames) { | |
if (err) return this.error(err); | |
this.pass(directory, fileNames); | |
var group = this.makeGroup(); | |
fileNames.forEach(function (name) { | |
FS.stat(name, group.slot()); | |
}); | |
}, | |
function (err, directory, filenames, stats) { | |
if (err) return this.error(err); | |
var output = {}; | |
filenames.forEach(function (name, i) { | |
var path = Path.join(directory, name); | |
output[path] = stats[i]; | |
}); | |
this.pass(output); | |
} | |
); | |
statdir(__dirname, function (err, stats) { | |
if (err) throw err; | |
console.log("Stats", stats); | |
}) |
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 TwoStep = require('./twostep'); | |
var FS = require('fs'); | |
TwoStep( | |
function one() { | |
this.pass(__filename + ".bak"); | |
FS.readFile(__filename, 'utf8', this.slot()); | |
}, | |
function two(err, target, contents) { | |
if (err) throw err; | |
this.pass(target); | |
FS.writeFile(target, contents, this.slot()) | |
}, | |
function three(err, target) { | |
if (err) throw err; | |
console.log("%s written to successfully", target); | |
FS.readdir(__dirname, this.slot()); | |
}, | |
function four(err, fileNames) { | |
if (err) throw err; | |
this.pass(fileNames); | |
var group = this.makeGroup(); | |
fileNames.forEach(function (filename) { | |
FS.stat(filename, group.slot()); | |
}); | |
}, | |
function five(err, fileNames, stats) { | |
if (err) throw err; | |
this.pass(fileNames.filter(function (name, i) { | |
return stats[i].isFile(); | |
})); | |
var group = this.makeGroup(); | |
stats.forEach(function (stat, i) { | |
if (stat.isFile()) FS.readFile(fileNames[i], 'utf8', group.slot()); | |
}); | |
}, | |
function six(err, fileNames, contents) { | |
if (err) throw err; | |
var merged = {}; | |
fileNames.forEach(function (name, i) { | |
merged[name] = contents[i].substr(0, 80); | |
}); | |
console.log(merged); | |
} | |
); |
We (@GameClosure) loved the ideas behind this gist, so we made our own "fork" of it called FF. We expanded on TwoStep by adding immediate failure and success calls (skipping the rest of the function chain), and we made the return object promise compatible. We are actively maintaining it currently, so feedback is highly welcomed.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
My attemp (I ported test from Step, but it doesn't pass all yet). Step is anyway much simpler and elegant then
async
. It covers 80% of use cases. For thoes 20% we can expand API if it suits.