Name: js-handler/node_modules/nodeunit/node_modules/tap/node_modules/slide/README.md
| 1: | # Controlling Flow: callbacks are easy |
| 2: | |
| 3: | ## What's actually hard? |
| 4: | |
| 5: | - Doing a bunch of things in a specific order. |
| 6: | - Knowing when stuff is done. |
| 7: | - Handling failures. |
| 8: | - Breaking up functionality into parts (avoid nested inline callbacks) |
| 9: | |
| 10: | |
| 11: | ## Common Mistakes |
| 12: | |
| 13: | - Abandoning convention and consistency. |
| 14: | - Putting all callbacks inline. |
| 15: | - Using libraries without grokking them. |
| 16: | - Trying to make async code look sync. |
| 17: | |
| 18: | ## Define Conventions |
| 19: | |
| 20: | - Two kinds of functions: *actors* take action, *callbacks* get results. |
| 21: | - Essentially the continuation pattern. Resulting code *looks* similar |
| 22: | to fibers, but is *much* simpler to implement. |
| 23: | - Node works this way in the lowlevel APIs already, and it's very flexible. |
| 24: | |
| 25: | ## Callbacks |
| 26: | |
| 27: | - Simple responders |
| 28: | - Must always be prepared to handle errors, that's why it's the first argument. |
| 29: | - Often inline anonymous, but not always. |
| 30: | - Can trap and call other callbacks with modified data, or pass errors upwards. |
| 31: | |
| 32: | ## Actors |
| 33: | |
| 34: | - Last argument is a callback. |
| 35: | - If any error occurs, and can't be handled, pass it to the callback and return. |
| 36: | - Must not throw. Return value ignored. |
| 37: | - return x ==> return cb(null, x) |
| 38: | - throw er ==> return cb(er) |
| 39: | |
| 40: | ```javascript |
| 41: | // return true if a path is either |
| 42: | // a symlink or a directory. |
| 43: | function isLinkOrDir (path, cb) { |
| 44: | fs.lstat(path, function (er, s) { |
| 45: | if (er) return cb(er) |
| 46: | return cb(null, s.isDirectory() || s.isSymbolicLink()) |
| 47: | }) |
| 48: | } |
| 49: | ``` |
| 50: | |
| 51: | # asyncMap |
| 52: | |
| 53: | ## Usecases |
| 54: | |
| 55: | - I have a list of 10 files, and need to read all of them, and then continue when they're all done. |
| 56: | - I have a dozen URLs, and need to fetch them all, and then continue when they're all done. |
| 57: | - I have 4 connected users, and need to send a message to all of them, and then continue when that's done. |
| 58: | - 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. |
| 59: | |
| 60: | |
| 61: | ## Solution |
| 62: | |
| 63: | ```javascript |
| 64: | var asyncMap = require("slide").asyncMap |
| 65: | function writeFiles (files, what, cb) { |
| 66: | asyncMap(files, function (f, cb) { |
| 67: | fs.writeFile(f, what, cb) |
| 68: | }, cb) |
| 69: | } |
| 70: | writeFiles([my, file, list], "foo", cb) |
| 71: | ``` |
| 72: | |
| 73: | # chain |
| 74: | |
| 75: | ## Usecases |
| 76: | |
| 77: | - I have to do a bunch of things, in order. Get db credentials out of a file, |
| 78: | read the data from the db, write that data to another file. |
| 79: | - If anything fails, do not continue. |
| 80: | - I still have to provide an array of functions, which is a lot of boilerplate, |
| 81: | and a pita if your functions take args like |
| 82: | |
| 83: | ```javascript |
| 84: | function (cb) { |
| 85: | blah(a, b, c, cb) |
| 86: | } |
| 87: | ``` |
| 88: | |
| 89: | - Results are discarded, which is a bit lame. |
| 90: | - No way to branch. |
| 91: | |
| 92: | ## Solution |
| 93: | |
| 94: | - reduces boilerplate by converting an array of [fn, args] to an actor |
| 95: | that takes no arguments (except cb) |
| 96: | - A bit like Function#bind, but tailored for our use-case. |
| 97: | - bindActor(obj, "method", a, b, c) |
| 98: | - bindActor(fn, a, b, c) |
| 99: | - bindActor(obj, fn, a, b, c) |
| 100: | - branching, skipping over falsey arguments |
| 101: | |
| 102: | ```javascript |
| 103: | chain([ |
| 104: | doThing && [thing, a, b, c] |
| 105: | , isFoo && [doFoo, "foo"] |
| 106: | , subChain && [chain, [one, two]] |
| 107: | ], cb) |
| 108: | ``` |
| 109: | |
| 110: | - tracking results: results are stored in an optional array passed as argument, |
| 111: | last result is always in results[results.length - 1]. |
| 112: | - treat chain.first and chain.last as placeholders for the first/last |
| 113: | result up until that point. |
| 114: | |
| 115: | |
| 116: | ## Non-trivial example |
| 117: | |
| 118: | - Read number files in a directory |
| 119: | - Add the results together |
| 120: | - Ping a web service with the result |
| 121: | - Write the response to a file |
| 122: | - Delete the number files |
| 123: | |
| 124: | ```javascript |
| 125: | var chain = require("slide").chain |
| 126: | function myProgram (cb) { |
| 127: | var res = [], last = chain.last, first = chain.first |
| 128: | chain([ |
| 129: | [fs, "readdir", "the-directory"] |
| 130: | , [readFiles, "the-directory", last] |
| 131: | , [sum, last] |
| 132: | , [ping, "POST", "example.com", 80, "/foo", last] |
| 133: | , [fs, "writeFile", "result.txt", last] |
| 134: | , [rmFiles, "./the-directory", first] |
| 135: | ], res, cb) |
| 136: | } |
| 137: | ``` |
| 138: | |
| 139: | # Conclusion: Convention Profits |
| 140: | |
| 141: | - Consistent API from top to bottom. |
| 142: | - Sneak in at any point to inject functionality. Testable, reusable, ... |
| 143: | - When ruby and python users whine, you can smile condescendingly. |
