]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.maps.server/node/node-v4.8.0-win-x64/node_modules/npm/node_modules/slide/README.md
Adding integrated tile server
[simantics/district.git] / org.simantics.maps.server / node / node-v4.8.0-win-x64 / node_modules / npm / 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 ď¬‚exible.
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.