Skip to content

Instantly share code, notes, and snippets.

@vojto
Created August 13, 2011 15:27
Show Gist options
  • Select an option

  • Save vojto/1143951 to your computer and use it in GitHub Desktop.

Select an option

Save vojto/1143951 to your computer and use it in GitHub Desktop.
Improve your asynchronous JavaScript code

Improve your asynchronous JavaScript code

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.

CoffeeScript (de)structuring

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}

Calling callback and exiting function

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

Assign your callback to local variables

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

Async is underscore.js but for asynchronous code.

For Loop

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.

Series

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment