While trying to improve my Node.js code by studying code of others, I realized that Pow is written in JavaScript. Even better, CoffeeScript.
And of course, nothing is easier to read than annotated source code generated by Docco.
Let me show you a couple of tricks I learned from their code.
This doesn't have much to do with asynchronous code, so let's just mention it and move on. Destructuring object into local variables:
{foo, bar} = {foo: "Foo", bar: "Bar"} // foo = "Foo", bar = "Bar"
Creating object from local variables:
baz = {foo, bar} // baz = {foo: foo, bar: bar}
This one is rather obvious, yet it didn't occur to me. (I blame Objective-C)
When an errors occurs in your asynchronous function, you want to send the error to callback, and exit the function, something like this:
if error
callback(error)
return
In most cases your functions work asynchronously and the return value is irrelevant. So why not do it in one line:
return callback(error) if error
You will end up nesting many callback functions inside other callback functions. Luckily, in CoffeeScript that's just more indentation. Still, that can be ugly at times.
Have you thought of assigning these callback functions to variables and passing those instead of anonymous functions?
Here's a piece of my code:
collection: (collection_name, callback) ->
if this.isConnected()
@client.collection collection_name, (coll_err, collection) =>
callback(collection)
else
@client.open (err, client) =>
@client.collection collection_name, (coll_err, collection) =>
callback(collection)
And here's how I improved this piece of code:
collection: (collection_name, callback) ->
proceed = =>
@client.collection collection_name, (coll_err, collection) =>
callback(collection)
return proceed() if this.isConnected()
@client.open (err, client) =>
proceed()
Same idea is used in Pow's Daemon.
Async is underscore.js but for asynchronous code.
I had to do something with a bunch of items in an array (map the items of an array, really) and return the resulting array. Here's my first try:
parseWithThings: (things, callback) =>
parsedThings = []
for thing in things
@parseThing thing, () =>
parsedThings.push thing
callback(parsedThings) if parsedThings.length == things.length
And here's same snippet using Async's map:
parseWithThings: (things, callback) =>
async.map things, (thing, next) =>
@parseThing thing, (parsedThing) =>
next null, parsedThing # error = null
, (error, parsedThings) =>
callback parsedThings
My first try would create a new array and compare lengths -- that just felt weird. While this code doesn't seem much simpler, it fits the asynchronous environment much better.
Here's a snippet from Pow's Installer.
install: (callback) ->
async.series [
@vivifyPath,
@writeFile,
@setOwnership,
@setPermissions
], callback
I didn't find any use of this in my own project, but I can imagine this being very useful, thus I mention it.