TL; DR: Use fibers to escape from callback hell!

As someone whose only experience with asynchronous functions was jQuery’s $.ajax I was in for a treat when I started working on a project using node.js. Functions that took functions as arguments? “That’s okay,” I told myself, “I’ve used functions like map and filter.” Little did I know what a mess this could quickly turn into!

The first few weeks weren’t that bad, it was still a new toy that I was playing with! I was then faced with a task where I had to make a database call only if a certain condition was met. Straightforward right? Nope! If things were synchronous you’d just do this:

if (studentName === undefined) {
  studentName = getDefaultName()
}
student = getStudent(studentName)

But getDefaultName is an asynchronous function! So I ended up doing this:

getDefaultName(studentName, function(data) {
  getStudent(data)
}

And..

function getDefaultName(studentName, callback) {
  if (studentName) {
    callback(studentName)
  }
  actualDbCall(callback)
}

Throw in some error checking and repeat this a few times to end up with the best piece of code ever!

The next issue that I faced was when I no longer had to make some existing asynchronous calls, and had to make some new asynchronous calls! Enter promises. I could add and remove function calls without messing up everything else. Optional function calls were still…annoying!

A wildly popular question and its many duplicates on Stack Overflow didn’t have a solution to handle this. At this point I started wondering if I was doing something so wrong that I was the only one facing this issue!

After looking at quite a few libraries like async I stumbled upon fibers. It had yield and run. Exactly what I needed: stop execution till some data is available and continue once it was available. I could now write the above function as:

if (studentName === undefined) {
  studentName = getDefaultName()
}
student = getStudent(studentName)

Just like the synchronous version! :) getDefaultName is defined as:

function getDefaultName(container) {
  var current = Fiber.current
  var result
  actualDbCall(function(name) {
    result = name
    current.run()
  })
  Fiber.yield()
  return result
}

There hasn’t been any noticeable change in performance by doing this but the code is so much more readable and modifiable after fibers came in. It’s a good option to have till async-await lands in node.js.