In case you do not know what a callback is, please refer to this awesome article by Hussein Faara.
All in all, callbacks are great. With callback, we are allowed to write
code in JavaScript asynchronously and it just work. However, in real world, as a Software Engineer, we do not simply expect things to just work and be satisfied with it. We also look at other things. For example, maintainability.
Let’s work with a real world example here to demonstrate what we want to talk about, say, we would like to add couple of songs to a playlist on Spotify.
Here are the steps that we need,
Retrieve temporary access token
Retrieve user’s id using the access token that we just got
Create a brand new empty playlist
Try to look for the song on Spotify for every song on the list
Since we got the user’s id from step 2 as well as the playlist’s id from step 3, we should now be able to add songs to the playlist on Spotify
As you can see, as we go further down the list, we seem to alway need something from the previous step. Without the information from above, we cannot really do anything. This is where the callback would be useful.
To be honest, this code looks long and ugly with 5 nested callbacks. Imagine 10+ nested callbacks, that, will be a lot more difficult to follow. So, what do we do? Are there any better way of doing it? Maybe the real question here is what is the right way to manage nested callbacks? or you prefer to call it as callback hell?
Here are the ways that I can think of that will help make it either cleaner or
a LOT cleaner,
Write comments
Split functions into smaller functions (Still using callback)
Use Promises
Use Async/await
First solution to the callback hell: Write comments
I have to admit, this is MUCH MUCH cleaner than it used to be and looks a LOT nicer.
But hey, don’t just stop here. As a Software Engineer, we always need to keep looking to see if there are any other way (maybe better) of doing the same thing.
Let’s look at the Promises approach. (and we can decide which way is better)
Third solution to the callback hell: Use Promises
Don’t know what a Promises is? Please read this awesome documentation about Promises by Mozilla.
Callback hell has truly disappear. This solution has the advantage of the second solution where it split functions into smaller functions but it also
avoids the use of nested functions thanks to the nature of Promises.
Let’s see how the Async/await different from Promises.
Forth solution to the callback hell: Use Async/await
Don’t know what an Async/await is? Please read this awesome documentation about Async/await by Mozilla.
One HUGE advantage of using Async/await to solve the callback hell is that you can write asynchronous JavaScript as if it was synchronous! Damn, just damn. Simply add Async/await keyword in front of your asynchronous functions and that is it. (Make sure your function returns Promises though) Thank you ECMAScript 2016 (ES7) for providing this awesome feature that everyone would love!!!
This is what I would recommend
If you were to ask me how I would personally handle the callback hell, this is what I would do. I certainly won’t recommend to jump from zero to step 3/4 as that might be a huge change to your code base. (and that often introduces new bug) What I would do is, incremental changes. Baby step. Start from writing comments to make it clearer for other developers then gradually split functions into smaller functions. Here, you should also reuse your functions as much as you can so that you do not have duplicate code. You should also keep your function as pure as possible. Every step of the way, you should write more tests as you see fit/missing. Run tests to make sure you did not break anything. Also ensure that you write code the way your team want. Use tool like TSLint to help you check if your code aligns well with your team lint rules on popular code editor like Visual Studio Code. Next, simply convert your “smaller functions” into Promises. So that you can either do step 3 or 4 later on easily. As you can see, I always do incremental changes instead of committing a huge chunk of code. Huge PR often gives your team member/code reviewer headache. Just don’t do it unless there is a good reason for it. Incremental changes are MUCH MUCH better as it also allows you to be agile and able to fix mistake quickly. Last but not least, I actually did this refactor myself for one of the project that I did (I told you it was a real world example) as I got annoyed by looking at the callback hell that I created. My project is called google-play-music-playlist-exporter hosted on GitHub and the transformation (GitHub commit) between callback hell and Promises is here. If I can do it, you can as well!!!
Okay, do let me know in the comments below if you have any questions/concerns and I would be happy to help in any way. Good luck in fixing your callback hell!
Wrapping Up
Hopefully you enjoyed this post. Let me know if this helps you. Thank you for reading!
Resources
I’ll try to keep this list current and up to date. If you know of a great resource you’d like to share or notice a broken link, please let us know.
Comments