Callback hell happens when we want to pass the result of the first async operation to the second async function and to the third and so on:

function myLongOperation(userId, callback) {

db.getUser(userId, (error, user) => {

if(!!error)

return callback(error, null)

else

api.generateMessage(user, (error, message) => {

if(!!error)

return callback(error, null)

else

client.sendMessage(message, callback)

})

})

}

Here we are passing userId to getUser in order to get the user asynchronously then we’re passing the user to generateMessage to … You know instead of narrating it in words let’s use some notation to describe this process:

userId → (getUser ⋙ generateMessage ⋙ sendMessage)

The above notation perfectly describes what our myLongOperation function does. Error handling at every step is clearly redundant. Promise fans know that this notation is very similar to (but not exactly the same as) what we do with Promises:

getUser(userId).then(generateMessage).then(sendMessage)

Promise.then takes care of error handling and chaining.

But our goal is to come up with a construct that is more general than Promises.

In our notation ⋙ is a way of composing (piping async functions). We will discuss it later.

x → y denote a function from x to y. For example:

const plus1 = x => x + 1

plus1 :: Number → Number

myLongOperation is a function from userId to a series of async operations, hence:

userId → ( … ⋙ … ⋙ … )

Haskellers know that this is not a proper type definition. But for our purpose this notation perfectly describes myLongOperation function.

Composable Callback

Promises are not the only solution to the callback hell problem. Promises provide more features than composability (for example they have an internal state which remembers if they’ve been resolved or not plus some other kinks).

Let’s define a bare minimum solution to the callback hell problem by implementing a “composable Callback” class:

Check out the full code here.

Callback class provides this interface:

constructor takes an async function (f which will produce either an error or a value x)

takes an async function (f which will produce either an error or a value x) run instance function: receives a callback function and feed it to the f

instance function: receives a callback function and feed it to the f map instance function analogous to Array.map, transforms the x (the result of f)

instance function analogous to Array.map, transforms the x (the result of f) bind instance function is similar to Promise.then, it is used for chaining Callback instances

instance function is similar to Promise.then, it is used for chaining Callback instances then instance function corresponds to Promise.then; it’s a combination of map and bind functions.

instance function corresponds to Promise.then; it’s a combination of map and bind functions. bindTo instance function is a utility for chaining Callback instances to normal async functions

instance function is a utility for chaining Callback instances to normal async functions pure (alias resolve) static function is similar to Promise.resolve, it creates an instance of Callback.

(alias resolve) static function is similar to Promise.resolve, it creates an instance of Callback. from static function casts an async function to an instance of Callback.

It’s not an accident that Callback interface resembles the interface of Promise. pure is an alias for resolve. If you’ve ever used Promise.resolve() you know what Callback.pure does. I think pure is a better name for our Callback class. Similarly Callback.then is analogous to Promise.then. I consciously avoid Callback.map and Callback.bind functions in this post, because Callback.then is sufficient as it both maps and binds.

We start with Callback.pure. It puts a value into a new Callback instance:

Callback.pure(64).run((error, result) => console.log(result))

Will log 64 in the Console.

This is how we can compose Callback.pure(64) with our sqrt function (JSBin):

Callback.pure(64)

.bindTo(sqrt)

.run((error, result) => console.log(error || result))

Under the hood, bindTo casts sqrt to an instance of Callback. The above snippet is equivalent to the following:

Callback.pure(36)

.then(x => new Callback(cb => sqrt(x, cb)))

.run((error, result) => console.log(error || result))

Using Callback class our myLongOperation function can be written more concisely as:

// userId → (getUser ⋙ genMessage ⋙ sendMessage) const myLongOperation = (userId, callback) =>

Callback.pure(userId)

.bindTo(getUser).bindTo(genMesssage).bindTo(sendMessage)

.run(callback)

Notice how closely this implementation matches the notation.

.bindTo(getUser).bindTo(genMesssage).bindTo(sendMessage) is denoted by (getUser ⋙ genMessage ⋙ sendMessage)

But Callback.pure(userId) seems unnecessary. (userId → (…) is the denotation of the whole myLongOperation function.) We will back to this point later.

Our changes to myLongOperation function are not visible to the user of this function. myLongOperation is still an async function that takes a userId and a callback.

We can always use bindTo utility to chain Callback instances to async functions. For example let’s assume we have another async function like getUserId(userName, callback) which we want to pipe its result into myLongOperation:

const messageUser = (userName, callback) =>

Callback.pure(userName).bindTo(getUserId).bindTo(myLongOperation)

.run(callback)

Notice that now run() is being called twice: once inside myLongOperation and the second time inside messageUser. There’s a catch here. Nothing really happens unless we call run().

const proc = Callback.pure(5)

.then(x => new Callback(cb => {

console.log(`binding ${x} to x + 1`)

setTimeout(() => cb(null, x + 1), 100)

}))

console.log() in the third line only happens after we call proc.run(). Try it here (JSBin).

proc (as an instance of Callback class) represents the instructions to an async operation that JavaScript only executes after run() is called. This is very different from Promises:

const prom = new Promise(resolve => {

console.log('Promise executes immediately')

resolve()

})

When you run this snippet, ‘Promise executes immediately’ is logged immediately, even if you never use the prom or prom.then(x => …).

So let’s change our myLongOperation function to return an instance of Callback (we can save one call to run() this way):

// userId → (getUser ⋙ genMessage ⋙ sendMessage) const myLongOperation = userId =>

Callback.pure(userId)

.bindTo(getUser).bindTo(genMesssage).bindTo(sendMessage)

Now this definition matches the notation even better since we eliminated the callback function completely.

In the same spirit, we update our messageUser function:

// userName → (getUserId ⋙ myLongOperation) const messageUser = userName =>

Callback.pure(userName).bindTo(getUserId).then(myLongOperation)

We changed the last bindTo() to then(), because now our updated myLongOperation is a function that returns an instance of Callback (remember originally before the change, it was a void function that was taking a callback in its second argument).

This is how we can use messageUser :

messageUser(userName).run((error, result) => ...)

We call run() only at the end of the operation. run() executes the operation and returns the result in its callback argument.

We achieved composability and avoided callback hell without resorting to Promises. Check out the full example here (JSBin).

Callback and Promise are Monads

Our Callback class and the standard Promise class have a lot in common. We call these constructs monad, by which I mean they have a bind (then) function that chains an instance of Callback (or Promise) to a function that returns another instance of Callback (or Promise).

const proc = Callback.pure(10)

proc.bind(x => new Callback(…))

We use this notation to describe proc as an instance of Callback monad:

proc :: Callback x

proc.bind :: (x → Callback y) → Callback y

We might read the notation like this:

proc is a Callback of x

proc.bind is a (higher order) function that takes a function from x to Callback of y and produces a Callback of y.

For example:

Callback.pure(10) :: Callback 10

It can be bound to a function that takes a Number and returns a new Callback:

Callback.pure(10)

.bind(x => new Callback(cb => cb(null, x + 1)))

(remember that resolve() is an alias for pure() and then() has similar functionality to bind())

Promise class also forms a monad:

Promise.resolve(10)

.then(x => new Promise(resolve => resolve(x + 1)))

These two expressions look vary similar and that’s indeed the power of monads. Monads provide an abstraction that is useful in many different programs. In our notation the above expressions can be written as:

Monad 10 ≫= (x → Monad (x + 1)) = Monad 11

For Promise Monad:

Monad 10 :: Promise.resolve(10)

≫= :: .then(…)

x → Monad (x + 1) :: x => new Promise(resolve => resolve(x + 1))

For Callback Monad:

Monad 10 :: Callback.resolve(10) // = Callback.pure(10)

≫= :: .then(…) // = Callback.bind(…)

x → Monad (x + 1) :: x => new Callback(cb => cb(x + 1))

Monads encapsulate a value that can only be retrieved by executing the monad. For Promise monad we retrieve the result of the computation (11) by calling the then() function and for our Callback monad we retrieve the result by run().

Monads have this interesting feature that they can be used even if their encapsulated value is not computed yet. We are able to call then() on a Promise and chain it with a function or another Promise even if it’s not completed and the value that it encapsulates is not computed yet. This fact is even more pronounced for our Callback monad. We had seen earlier that Callback doesn’t even bother start computing its result before we call run() (JSBin demo).

More generally both computations might be denoted as:

Monad x ≫= (x → Monad y) = Monad y

x and y can be of any type. Here they’re Numbers, but they can be String, Boolean, JSON objects, … or even functions or other monads!.

What is a Monad?

For our purpose any class that has these two features is a Monad:

The class must have a way of encapsulating a value (using a static pure() or resolve() function)

It must provide a way to bind itself with a function that returns another instance of it (using bind() or then())

Monads add extra structure to the value that they’re encapsulating. Different types of Monads provide different structures. The implementation of the pure function is the place to look for these structures.

For Promise:

Promise.resolve = x => new Promise(res => res(x))

For Callback:

Callback.pure = x => new Callback(cb => cb(null, x))

For Array:

Array.of = x => [x]

For Reader:

Reader.pure = x => new Reader(env => x)

Click on the links to see the definitions and play with these monads. In this post we only study Promise and Callback.

We can indeed define a monad that has almost no extra structure. This minimum monad is called Identity Monad:

Identity.pure = x => new Identity(x)

How Identity is useful is the subject of another post.