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)
 
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
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
 
- 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
 
- 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
 
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.