Fun With Formatting Async Code

Note: This post may be more about personal taste than anything.

Everyone encounters the pyramid of doom problem at some point when working with Node. One popular way to address this by using an async flow control library such as caolan's async. So, I got on board with this library and used it to write some unit tests which needed to deal with a series of file accesses. In particular, I was writing a file watcher. One of the tests looked like this

async.series([
    function(next){
        test.touchFile('one.txt', next)
    }, 
    function(next){
        test.touchFile('two.txt', next)
    }, 
    function(next){
        watcher.add(test.filePath('*.txt'))
        next()
    }, 
    function(next){
        setTimeout(next, 200)
    }, 
    function(next){
        test.touchFile('one.txt', next)
    }, 
    function(next){
        expect(changed.args[0])
            .to.be.eql(['change', test.filePath('one.txt')])
        next()
    }, 
    function(next){
        test.touchFile('two.txt', next)
    }, 
    function(next){
        expect(changed.args[1])
            .to.be.eql(['change', test.filePath('two.txt')])
        done()
    }
])

Something about this code doesn't sit well with me, and it's not just that it's verbose - which it definitely is. It is the fact that the actual code of interest, for example

    test.touchFile('one.txt', next)

is indented underneath this uninteresting line of boilerplate

function(next){

It's burying the lead, and this makes scanning the code difficult. Maybe we can format the code in a way that makes the interesting lines more prominent. Something like this

async.series([function(next){
    test.touchFile('one.txt', next) }, function(next){
    test.touchFile('two.txt', next) }, function(next){
    watcher.add(test.filePath('*.txt')), next() }, function(next){
    setTimeout(next, 200) }, function(next){
    test.touchFile('one.txt', next) }, function(next){
    expect(changed.args[0])
      .to.be.eql(['change', test.filePath('one.txt')]), next() }, function(next){
    test.touchFile('two.txt', next) }, function(next){
    expect(changed.args[1])
      .to.be.eql(['change', test.filePath('two.txt')])
    done() }
])

By tucking the uninteresting part: function(next){ to the back, the interesting parts emerge to the beginning and now the program is easily scannable. At first glance, it almost looks like normal straight line Javascript.

However, there are a couple of things I don't like about this

  1. maybe it looks too much like normal script, having it look somewhat different would make the async style code stand out
  2. the fact that most lines end in a curly brace looks kinda noisy

So, I moved the curly down a line.

async.series([function(next)

{ test.touchFile('one.txt', next) }, function(next)
{ test.touchFile('two.txt', next) }, function(next)
{ watcher.add(test.filePath('*.txt')), next() }, function(next)
{ setTimeout(next, 200) }, function(next)
{ test.touchFile('one.txt', next) }, function(next)
{ expect(changed.args[0])
      .to.be.eql(['change', test.filePath('one.txt')]), next() }, function(next)
{ test.touchFile('two.txt', next) }, function(next)
{ expect(changed.args[1])
      .to.be.eql(['change', test.filePath('two.txt')])
  done() }

])

Now, this looks interesting. Each line is a line of Javascript wrapped by curly braces. This looks foreign and familiar at the same time - almost like another language. A good analogy is the IO Monad in Haskell, or the method chaining syntax of jQuery. And it also looks cleaner than my last attempt because there are no braces left unclosed on any line. I like it and I am going to try it out.

blog comments powered by Disqus