pax_global_header00006660000000000000000000000064121375112330014510gustar00rootroot0000000000000052 comment=820db4fdd463043e9e22780ed2772c8023300fbc slide-flow-control-1.1.4/000077500000000000000000000000001213751123300152365ustar00rootroot00000000000000slide-flow-control-1.1.4/LICENSE000066400000000000000000000021041213751123300162400ustar00rootroot00000000000000Copyright 2009, 2010, 2011 Isaac Z. Schlueter. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. slide-flow-control-1.1.4/README.md000066400000000000000000000076551213751123300165320ustar00rootroot00000000000000# Controlling Flow: callbacks are easy ## What's actually hard? - Doing a bunch of things in a specific order. - Knowing when stuff is done. - Handling failures. - Breaking up functionality into parts (avoid nested inline callbacks) ## Common Mistakes - Abandoning convention and consistency. - Putting all callbacks inline. - Using libraries without grokking them. - Trying to make async code look sync. ## Define Conventions - Two kinds of functions: *actors* take action, *callbacks* get results. - Essentially the continuation pattern. Resulting code *looks* similar to fibers, but is *much* simpler to implement. - Node works this way in the lowlevel APIs already, and it's very flexible. ## Callbacks - Simple responders - Must always be prepared to handle errors, that's why it's the first argument. - Often inline anonymous, but not always. - Can trap and call other callbacks with modified data, or pass errors upwards. ## Actors - Last argument is a callback. - If any error occurs, and can't be handled, pass it to the callback and return. - Must not throw. Return value ignored. - return x ==> return cb(null, x) - throw er ==> return cb(er) ```javascript // return true if a path is either // a symlink or a directory. function isLinkOrDir (path, cb) { fs.lstat(path, function (er, s) { if (er) return cb(er) return cb(null, s.isDirectory() || s.isSymbolicLink()) }) } ``` # asyncMap ## Usecases - I have a list of 10 files, and need to read all of them, and then continue when they're all done. - I have a dozen URLs, and need to fetch them all, and then continue when they're all done. - I have 4 connected users, and need to send a message to all of them, and then continue when that's done. - I have a list of n things, and I need to dosomething with all of them, in parallel, and get the results once they're all complete. ## Solution ```javascript var asyncMap = require("slide").asyncMap function writeFiles (files, what, cb) { asyncMap(files, function (f, cb) { fs.writeFile(f, what, cb) }, cb) } writeFiles([my, file, list], "foo", cb) ``` # chain ## Usecases - I have to do a bunch of things, in order. Get db credentials out of a file, read the data from the db, write that data to another file. - If anything fails, do not continue. - I still have to provide an array of functions, which is a lot of boilerplate, and a pita if your functions take args like ```javascript function (cb) { blah(a, b, c, cb) } ``` - Results are discarded, which is a bit lame. - No way to branch. ## Solution - reduces boilerplate by converting an array of [fn, args] to an actor that takes no arguments (except cb) - A bit like Function#bind, but tailored for our use-case. - bindActor(obj, "method", a, b, c) - bindActor(fn, a, b, c) - bindActor(obj, fn, a, b, c) - branching, skipping over falsey arguments ```javascript chain([ doThing && [thing, a, b, c] , isFoo && [doFoo, "foo"] , subChain && [chain, [one, two]] ], cb) ``` - tracking results: results are stored in an optional array passed as argument, last result is always in results[results.length - 1]. - treat chain.first and chain.last as placeholders for the first/last result up until that point. ## Non-trivial example - Read number files in a directory - Add the results together - Ping a web service with the result - Write the response to a file - Delete the number files ```javascript var chain = require("slide").chain function myProgram (cb) { var res = [], last = chain.last, first = chain.first chain([ [fs, "readdir", "the-directory"] , [readFiles, "the-directory", last] , [sum, last] , [ping, "POST", "example.com", 80, "/foo", last] , [fs, "writeFile", "result.txt", last] , [rmFiles, "./the-directory", first] ], res, cb) } ``` # Conclusion: Convention Profits - Consistent API from top to bottom. - Sneak in at any point to inject functionality. Testable, reusable, ... - When ruby and python users whine, you can smile condescendingly. slide-flow-control-1.1.4/index.js000066400000000000000000000000461213751123300167030ustar00rootroot00000000000000module.exports=require("./lib/slide") slide-flow-control-1.1.4/lib/000077500000000000000000000000001213751123300160045ustar00rootroot00000000000000slide-flow-control-1.1.4/lib/async-map-ordered.js000066400000000000000000000033011213751123300216510ustar00rootroot00000000000000 throw new Error("TODO: Not yet implemented.") /* usage: Like asyncMap, but only can take a single cb, and guarantees the order of the results. */ module.exports = asyncMapOrdered function asyncMapOrdered (list, fn, cb_) { if (typeof cb_ !== "function") throw new Error( "No callback provided to asyncMapOrdered") if (typeof fn !== "function") throw new Error( "No map function provided to asyncMapOrdered") if (list === undefined || list === null) return cb_(null, []) if (!Array.isArray(list)) list = [list] if (!list.length) return cb_(null, []) var errState = null , l = list.length , a = l , res = [] , resCount = 0 , maxArgLen = 0 function cb (index) { return function () { if (errState) return var er = arguments[0] var argLen = arguments.length maxArgLen = Math.max(maxArgLen, argLen) res[index] = argLen === 1 ? [er] : Array.apply(null, arguments) // see if any new things have been added. if (list.length > l) { var newList = list.slice(l) a += (list.length - l) var oldLen = l l = list.length process.nextTick(function () { newList.forEach(function (ar, i) { fn(ar, cb(i + oldLen)) }) }) } if (er || --a === 0) { errState = er cb_.apply(null, [errState].concat(flip(res, resCount, maxArgLen))) } }} // expect the supplied cb function to be called // "n" times for each thing in the array. list.forEach(function (ar) { steps.forEach(function (fn, i) { fn(ar, cb(i)) }) }) } function flip (res, resCount, argLen) { var flat = [] // res = [[er, x, y], [er, x1, y1], [er, x2, y2, z2]] // return [[x, x1, x2], [y, y1, y2], [undefined, undefined, z2]] slide-flow-control-1.1.4/lib/async-map.js000066400000000000000000000027301213751123300202340ustar00rootroot00000000000000 /* usage: // do something to a list of things asyncMap(myListOfStuff, function (thing, cb) { doSomething(thing.foo, cb) }, cb) // do more than one thing to each item asyncMap(list, fooFn, barFn, cb) */ module.exports = asyncMap function asyncMap () { var steps = Array.prototype.slice.call(arguments) , list = steps.shift() || [] , cb_ = steps.pop() if (typeof cb_ !== "function") throw new Error( "No callback provided to asyncMap") if (!list) return cb_(null, []) if (!Array.isArray(list)) list = [list] var n = steps.length , data = [] // 2d array , errState = null , l = list.length , a = l * n if (!a) return cb_(null, []) function cb (er) { if (errState) return var argLen = arguments.length for (var i = 1; i < argLen; i ++) if (arguments[i] !== undefined) { data[i - 1] = (data[i - 1] || []).concat(arguments[i]) } // see if any new things have been added. if (list.length > l) { var newList = list.slice(l) a += (list.length - l) * n l = list.length process.nextTick(function () { newList.forEach(function (ar) { steps.forEach(function (fn) { fn(ar, cb) }) }) }) } if (er || --a === 0) { errState = er cb_.apply(null, [errState].concat(data)) } } // expect the supplied cb function to be called // "n" times for each thing in the array. list.forEach(function (ar) { steps.forEach(function (fn) { fn(ar, cb) }) }) } slide-flow-control-1.1.4/lib/bind-actor.js000066400000000000000000000005761213751123300203740ustar00rootroot00000000000000module.exports = bindActor function bindActor () { var args = Array.prototype.slice.call (arguments) // jswtf. , obj = null , fn if (typeof args[0] === "object") { obj = args.shift() fn = args.shift() if (typeof fn === "string") fn = obj[ fn ] } else fn = args.shift() return function (cb) { fn.apply(obj, args.concat(cb)) } } slide-flow-control-1.1.4/lib/chain.js000066400000000000000000000012241213751123300174230ustar00rootroot00000000000000module.exports = chain var bindActor = require("./bind-actor.js") chain.first = {} ; chain.last = {} function chain (things, cb) { var res = [] ;(function LOOP (i, len) { if (i >= len) return cb(null,res) if (Array.isArray(things[i])) things[i] = bindActor.apply(null, things[i].map(function(i){ return (i===chain.first) ? res[0] : (i===chain.last) ? res[res.length - 1] : i })) if (!things[i]) return LOOP(i + 1, len) things[i](function (er, data) { if (er) return cb(er, res) if (data !== undefined) res = res.concat(data) LOOP(i + 1, len) }) })(0, things.length) } slide-flow-control-1.1.4/lib/slide.js000066400000000000000000000001711213751123300174410ustar00rootroot00000000000000exports.asyncMap = require("./async-map") exports.bindActor = require("./bind-actor") exports.chain = require("./chain") slide-flow-control-1.1.4/package.json000066400000000000000000000010101213751123300175140ustar00rootroot00000000000000{ "name": "slide", "version": "1.1.4", "author": "Isaac Z. Schlueter (http://blog.izs.me/)", "contributors": [ "S. Sriram (http://www.565labs.com)" ], "description": "A flow control lib small enough to fit on in a slide presentation. Derived live at Oak.JS", "main": "./lib/slide.js", "dependencies": {}, "devDependencies": {}, "engines": { "node": "*" }, "repository": { "type": "git", "url": "git://github.com/isaacs/slide-flow-control.git" } }