Asynchronisity 1


I have been working with asynchronous code all my career – ever since I wrote a multi-processing kernel on top of DOS to support a phone system reporter.

My second most recent cycle was on node-js over the last 3 years. On the server the need for callbacks is 100-fold higher than in the browser. The problem is the same, but the need for nesting is much greater.

Don’t get me wrong, I like the JavaScript asynchronous model. It sends me back to 1993 and Windows “cooperative multi-tasking”. I like the fact that I have control – and have the responsibility not to hog the CPU and freeze the system. This is literal in the case of the browser where the JavaScript runs in the same process as the UI update. Mostly I love the fact that my data is my own. I know no other thread is going to diddle with it until I specifically call something asynchronous. Between that and closures and local variable, life is sweet (no, not a pun on sweet.js).

There is a term “callback hell” when callbacks are coded directly creating a horizontal pyramid of indentation.

action_one(params, function(results) {
    work...
    action_two(params, function(results) {
      work...
      action_three(params, function(results) {
          work...
          action_four(params, function(results) {
              work...
          })
      })
    })
})}

Just imagine how this would look with more code and more asynchronous functions. My first attempt was to use a library similar to async.js (https://github.com/fjakobs/async.js).

async.list([
    function(callback) {
        work...
        action_one(params, callback)
    },
    function(callback) {
        work...
        action_two(params, callback)
    },
    function(callback) {
        work...
        action_three(params, callback)
    },
    function(callback) {
        work...
        action_four(params, callback)
    }
]).call().end(work...)

Looks cleaner, doesn’t it? In practice, once code is added the sequential nature is lost and it is no easier to follow than raw callbacks.

So, let’s clean thing up the practical way – separating concerns (or in this case steps).

step_one = function(callback) {
    work...
    action_one(params, callback)
}

step_two = function(callback) {
    work...
    action_two(params, callback)
}

step_three = function(callback) {
    work...
    action_three(params, callback)
}

step_four = function() {
    work...
    action_four(params)
}

async.list([step_one, step_two, step_three, step_four]).call().end(...)

This made sense to me. Each step is an action that ends in an asynchronous call. By naming the steps well, the waterfall clearly documented the sequence.

Now wait, what is the benefit of waterfall. How would it look with raw callbacks.

step_one( step_two( step_three( step_four())))

It is just as clear without the overheads on an async library.

The particular async library I linked to also has many of the interfaces provided by frp and reactivex (http://reactivex.io/) and streams – map, reduce, filter, etc. I will talk about it next installment.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s