# [Node.js] How to Use Promises in a Callback-based Codebase

Hi! I'm Gaurang. I’ve worked extensively with Node.js in the past, and during that time, I spent a lot of effort wrangling with different async patterns — callbacks, promises, and async/await. This article is part of my series, *"*[*My guide to getting through the maze of callbacks, promises, and async-await*](https://blog.gaurang.page/series/nodejs-async-guide)*"*, where I share lessons learned and best practices for writing asynchronous code in Node.js.

This article assumes you’re familiar with basic Node.js concepts, especially asynchronous programming constructs like callbacks, promises, and async/await.

---

# Converting a Promise-based Function to Use Callbacks

Often, you’ll find yourself working in older codebases where asynchronous functions are written using the callback pattern. Node.js also offers a built-in way to convert a Promise-returning function into a callback-based one using `util.callbackify()`.

Personally, I believe we should avoid writing new callbacks whenever possible. Even in a callback-heavy codebase, it's often worth introducing promises or async/await for better readability and maintainability.  
I’ve spent years digging through callback-infested codebases, and I’ll say it plainly: callbacks are obsolete. They had their time, but that time is over. Stop writing them. Let them die.

Use this only when absolutely necessary — like when you're deep in a legacy callback jungle and need to drop in a Promise-based function without rewriting everything around it.

%[https://gist.github.com/gaurang847/38e6f62eff32ac2e3bfb444f74f13585.js?file=promise_in_callback] 

# Prefer Then-chaining over nesting

It's common to see people misuse promises by nesting `.then()` calls instead of chaining them. This leads to "Promise hell", which is structurally similar to "callback hell."

%[https://gist.github.com/gaurang847/38e6f62eff32ac2e3bfb444f74f13585.js?file=promise_hell] 

Callbacks are difficult to work with primarily because of their deeply nested structure. Promises were introduced to address this by enabling a more linear, manageable code flow.

Of course, code with then-chaining may not be as readable and intuitive as synchronous code or code written with async-await. But it is more manageable than code with nested thens.

For a deeper dive into why Promise hell is best avoided — and how to write cleaner promise chains — check out these excellent articles:

%[https://dev.to/somedood/please-don-t-nest-promises-3o1o] 

%[https://medium.com/@pyrolistical/how-to-get-out-of-promise-hell-8c20e0ab0513] 

Keep in mind that it’s not just `.then()` chains you need to keep clean — **don’t nest callback-based functions inside a** `.then()` either. This is a common mistake that defeats the purpose of using Promises in the first place.

If you’re already using Promises, don’t go back to callbacks mid-way. Promises are designed to give you a linear, manageable control flow — and the moment you reintroduce nested callbacks, you throw that benefit away. Whether you’re nesting callbacks or `.then()` blocks, the result is the same: harder-to-read code.

Instead, convert the callback-based function into a Promise using any of the methods discussed in my previous article “[Using callback-based functions when the rest of the code uses Promises](https://blog.gaurang.page/nodejs-using-callback-based-functions-when-the-rest-of-the-code-uses-promises)“ and keep your chain linear.

%[https://gist.github.com/gaurang847/38e6f62eff32ac2e3bfb444f74f13585.js?file=avoid_nesting_cb_in_promise] 

Notice that `readFile2CB` is a callback-based function. If you nest it (as shown in the bad-code example), any code that relies on `content2` or `results` must go *inside* that nested block. Over time, this leads to deeply indented, harder-to-read code — exactly the kind of mess Promises were created to prevent.

Once you start using Promises, commit to the pattern. Nesting defeats the purpose. Whether you’re nesting `.then()` blocks or stuffing callbacks inside them, you’re trading away clarity and maintainability — the very reasons Promises exist.

---

If you're stuck in a callback-heavy codebase, I feel your pain. Use these patterns sparingly, but push for modern async functions where you can — your future self (and your teammates) will thank you.

I've put a lot of work into this article. And I hope that it has been helpful. If you have any questions, please feel free to leave a comment below. I'd really appreciate it if you could like and share the article! 😊
