Boosting Customer Happiness with Science and Code

❮ Blog Home

Taming Callback Pyramids in Our AngularJS App Using Async/Await

April 23, 2020 By Raymond Sohn


Our dashboard is a medium-sized (~26k lines of code) AngularJS SPA (single-page application). Like most API driven SPAs, our dashboard makes a lot of asynchronous calls to fetch data. In many cases, we’re nesting asynchronous API calls several levels deep which has started to become a maintenance problem for our team. It’s not a total blocker, but it has slowed down development in complex parts of the code base.

Cue the Bat-Signal, because this is where async/await arrives to save the day. Async/await is an entirely new way to write asynchronous code in JavaScript but the concept of async/await is much older. It dates all the way back to 2010 and has its roots in the functional programming language of F#. And now, async/await can be found in many popular programming languages such as C#, Python, and Rust.

The biggest benefit of async/await is that asynchronous, non-blocking code can be written to look like synchronous, blocking code. To put it another way, with async/await we can flatten our deeply nested asynchronous code and thus make our code simpler to understand, debug, and develop.


Flattening Callback Pyramids

Above is an extremely pared-down example from our code base of a callback pyramid. 

Why are they called callback pyramids?

1. If you tilt your head, you’ll notice that the shape of the code outlines a triangle or a pyramid (if you want to Walk Like An Egyptian).

2. Notice how deeply nested the code is; this is inevitable when you handle asynchronous code with just callbacks.

Below is the same code,  rewritten using async/await.

Beautiful, isn’t it? The callback pyramid has been flattened, and the code is easier to read because with async/await we can treat asynchronous calls like synchronous function calls.


The Power Of Babel

In order to use async/await now, and not years from now when all browsers finally implement async/await, we used the Babel compiler which translates our futuristic JavaScript into JavaScript that older browsers can understand.

We’re huge fans of the Babel compiler because:

1. it allows us to write futuristic JavaScript today and take advantage of features like arrow functions, template strings, and classes.

2. it’s a stable piece of software and the generated code hasn’t caused us any headaches.

After a decade of programming, one of the lessons that I’ve learned is that “simple” things are usually hiding their complexity somewhere else. This is true of Babel and its implementation of async/await. Behind the deceptively simple syntax of async/await, lies a cave filled with a thousand dragon books.

Below is the async/await code from earlier, after it has been translated by Babel. I’m not a compiler wizard but I have built a few toy compilers, and it seems like Babel is transforming the async/await code into a Continuation-passing style. I applaud the Babel team because I can’t imagine how much work went into this feature.

Although it's a complex feature that ends up generating more code than the original non async/await example, I do think the trade-off is worth it. Developers can focus on building features while the machine takes care of the asynchronous plumbing.



I’m pleased to report that enabling async/await in Babel was a breeze and that async/await has been running in production for the past few weeks with no issues. Async/await has not only allowed us to tame our callback pyramids, but it has also changed the way that we think about asynchronous code. It’s always exciting to find a tool that makes your job a little bit easier, and it’s even better if you can share that knowledge with others.


Filed Under: Engineering