diff options
author | isaacs <i@izs.me> | 2013-04-29 19:45:38 +0400 |
---|---|---|
committer | isaacs <i@izs.me> | 2013-04-29 19:58:34 +0400 |
commit | d554bdb1b3383cbdab1ccab9ff4a83dfaaec4056 (patch) | |
tree | 24899427883272e4b655d2d95a518d60d00de6d9 /node_modules/slide | |
parent | 12c54d213e84b77e7914f37f9816e746bde483d2 (diff) |
slide@1.1.4
Diffstat (limited to 'node_modules/slide')
-rw-r--r-- | node_modules/slide/.npmignore | 1 | ||||
-rw-r--r-- | node_modules/slide/README.md | 153 | ||||
-rw-r--r-- | node_modules/slide/nodejs-controlling-flow.pdf | bin | 167502 -> 0 bytes | |||
-rw-r--r-- | node_modules/slide/package.json | 23 |
4 files changed, 151 insertions, 26 deletions
diff --git a/node_modules/slide/.npmignore b/node_modules/slide/.npmignore deleted file mode 100644 index a13633799..000000000 --- a/node_modules/slide/.npmignore +++ /dev/null @@ -1 +0,0 @@ -*.pdf diff --git a/node_modules/slide/README.md b/node_modules/slide/README.md index 6e4be2f94..59ad738bc 100644 --- a/node_modules/slide/README.md +++ b/node_modules/slide/README.md @@ -1,32 +1,143 @@ -# Slide - a tiny flow control library +# Controlling Flow: callbacks are easy -Callbacks are simple and easy if you keep the pattern consistent. +## What's actually hard? -Check out the [slide -presentation](http://github.com/isaacs/slide-flow-control/raw/master/nodejs-controlling-flow.pdf), -or the [blog post](http://howto.no.de/flow-control-in-npm). +- 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) -You'll laugh when you see how little code is actually in this thing. -It's so not-enterprisey, you won't believe it. It does almost nothing, -but it's super handy. -I use this util in [a real world program](http://npmjs.org/). +## Common Mistakes -You should use it as an example of how to write your own flow control -utilities. You'll never fully appreciate a flow control lib that you -didn't write yourself. +- Abandoning convention and consistency. +- Putting all callbacks inline. +- Using libraries without grokking them. +- Trying to make async code look sync. -## Installation +## Define Conventions -Just copy the files into your project, and use them that way, or -you can do this: +- 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. - npm install slide +## Callbacks -and then: +- 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. - var asyncMap = require("slide").asyncMap - , chain = require("slide").chain - // use the power! +## Actors -Enjoy! +- 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. diff --git a/node_modules/slide/nodejs-controlling-flow.pdf b/node_modules/slide/nodejs-controlling-flow.pdf Binary files differdeleted file mode 100644 index ca12d60cb..000000000 --- a/node_modules/slide/nodejs-controlling-flow.pdf +++ /dev/null diff --git a/node_modules/slide/package.json b/node_modules/slide/package.json index 5cc2689e6..0ec014041 100644 --- a/node_modules/slide/package.json +++ b/node_modules/slide/package.json @@ -1,9 +1,17 @@ { "name": "slide", - "version": "1.1.3", - "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)", + "version": "1.1.4", + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, "contributors": [ - "S. Sriram <ssriram@gmail.com> (http://www.565labs.com)" + { + "name": "S. Sriram", + "email": "ssriram@gmail.com", + "url": "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", @@ -15,5 +23,12 @@ "repository": { "type": "git", "url": "git://github.com/isaacs/slide-flow-control.git" - } + }, + "readme": "# Controlling Flow: callbacks are easy\n\n## What's actually hard?\n\n- Doing a bunch of things in a specific order.\n- Knowing when stuff is done.\n- Handling failures.\n- Breaking up functionality into parts (avoid nested inline callbacks)\n\n\n## Common Mistakes\n\n- Abandoning convention and consistency.\n- Putting all callbacks inline.\n- Using libraries without grokking them.\n- Trying to make async code look sync.\n\n## Define Conventions\n\n- Two kinds of functions: *actors* take action, *callbacks* get results.\n- Essentially the continuation pattern. Resulting code *looks* similar\n to fibers, but is *much* simpler to implement.\n- Node works this way in the lowlevel APIs already, and it's very flexible.\n\n## Callbacks\n\n- Simple responders\n- Must always be prepared to handle errors, that's why it's the first argument.\n- Often inline anonymous, but not always.\n- Can trap and call other callbacks with modified data, or pass errors upwards.\n\n## Actors\n\n- Last argument is a callback.\n- If any error occurs, and can't be handled, pass it to the callback and return.\n- Must not throw. Return value ignored.\n- return x ==> return cb(null, x)\n- throw er ==> return cb(er)\n\n```javascript\n// return true if a path is either\n// a symlink or a directory.\nfunction isLinkOrDir (path, cb) {\n fs.lstat(path, function (er, s) {\n if (er) return cb(er)\n return cb(null, s.isDirectory() || s.isSymbolicLink())\n })\n}\n```\n\n# asyncMap\n\n## Usecases\n\n- I have a list of 10 files, and need to read all of them, and then continue when they're all done.\n- I have a dozen URLs, and need to fetch them all, and then continue when they're all done.\n- I have 4 connected users, and need to send a message to all of them, and then continue when that's done.\n- 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.\n\n\n## Solution\n\n```javascript\nvar asyncMap = require(\"slide\").asyncMap\nfunction writeFiles (files, what, cb) {\n asyncMap(files, function (f, cb) {\n fs.writeFile(f, what, cb)\n }, cb)\n}\nwriteFiles([my, file, list], \"foo\", cb)\n```\n\n# chain\n\n## Usecases\n\n- I have to do a bunch of things, in order. Get db credentials out of a file,\n read the data from the db, write that data to another file.\n- If anything fails, do not continue.\n- I still have to provide an array of functions, which is a lot of boilerplate,\n and a pita if your functions take args like\n\n```javascript\nfunction (cb) {\n blah(a, b, c, cb)\n}\n```\n\n- Results are discarded, which is a bit lame.\n- No way to branch.\n\n## Solution\n\n- reduces boilerplate by converting an array of [fn, args] to an actor\n that takes no arguments (except cb)\n- A bit like Function#bind, but tailored for our use-case.\n- bindActor(obj, \"method\", a, b, c)\n- bindActor(fn, a, b, c)\n- bindActor(obj, fn, a, b, c)\n- branching, skipping over falsey arguments\n\n```javascript\nchain([\n doThing && [thing, a, b, c]\n, isFoo && [doFoo, \"foo\"]\n, subChain && [chain, [one, two]]\n], cb)\n```\n\n- tracking results: results are stored in an optional array passed as argument,\n last result is always in results[results.length - 1].\n- treat chain.first and chain.last as placeholders for the first/last\n result up until that point.\n\n\n## Non-trivial example\n\n- Read number files in a directory\n- Add the results together\n- Ping a web service with the result\n- Write the response to a file\n- Delete the number files\n\n```javascript\nvar chain = require(\"slide\").chain\nfunction myProgram (cb) {\n var res = [], last = chain.last, first = chain.first\n chain([\n [fs, \"readdir\", \"the-directory\"]\n , [readFiles, \"the-directory\", last]\n , [sum, last]\n , [ping, \"POST\", \"example.com\", 80, \"/foo\", last]\n , [fs, \"writeFile\", \"result.txt\", last]\n , [rmFiles, \"./the-directory\", first]\n ], res, cb)\n}\n```\n\n# Conclusion: Convention Profits\n\n- Consistent API from top to bottom.\n- Sneak in at any point to inject functionality. Testable, reusable, ...\n- When ruby and python users whine, you can smile condescendingly.\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/isaacs/slide-flow-control/issues" + }, + "_id": "slide@1.1.4", + "_from": "slide@latest" } |