The most typical asynchronous programming challenge is to make dozens of AJAX calls per page. How you choose to deal with asynchronous calls will decide the future of your application whether it will work well in the market or fail.
The most apparent solution came in the form of nested functions as callbacks. This solution led to a problem popularly known to developers as callback hell, making the code difficult to manage and debug. Even today, many applications feel the burn of it.
Then, we got Promises. Using this pattern, the code was now much easier to handle, but many cases of the same code were repeated to manage the application’s flow properly. This was in direct contrast to the Don’t Repeat Yourself (DRY) principle.
Go Through This Simple Task To Help Us Understand The Concepts:
- Verify the username and password of a user.
- Get tasks for the user.
- Log the user’s access time.
Approach 1: Callback/Callback Hell/The Pyramid of Doom
Using nested callbacks was the ancient approach that was used to synchronise the asynchronous code. With time, this approach lost its popularity as it made the code more complex, which affected the scalability of the app, all thanks to the issue known as callback hell.
Each function gets an argument which is another function that’s called with a parameter that’s the response of the previous action. As these functions tend to get more and more complex, the nesting starts to extend, and it becomes a nightmare for the developer to stay on track of the inner conditional statements and nesting.
Too many folks will experience headaches just by reading the code above. Having an application with many similar code blocks will cause even more trouble for the person maintaining the code, despite the fact that they wrote it themselves.
Once you realise that db.getTasks is another function containing nested callbacks, this example gets even more complicated.
Approach 2: Promises
For escaping callback hell, promises seemed to be the next logical step. Even though this method failed to remove callbacks’ utilisation, it made the chaining of functions straightforward and simplified the code, making it much easier to read.
What Is A Promise?
Promises play the role of placeholders for values that are unknown when it was created/formed. This approach allows the user to attach handlers to either the success value or the reason for the asynchronous action’s failure. This enables the asynchronous methods to return values as if they were synchronous. What this asynchronous method does is instead of directly returning the calculated value for a time-consuming operation, it provides a placeholder value and a promise to deliver the actual value at some point shortly when the time-consuming process finishes.
A promise has handlers that are queued up to its then method called when either a promise is fulfilled or rejected.
With promises in place, our code would look much cleaner and easier to understand.
To attain this form of simplicity, the functions used in the example need to be Promisified. Let’s take a look at how the getTasks method would be updated to return a Promise:
We have modified the getTasks method to return a Promise, with two callbacks, and the Promise itself performs actions from the technique. The reject and resolve callbacks will be assigned to promise.then and promise.catch methods, respectively.
You will find that the getTasks method is still internally prone to the pyramid of doom phenomenon. This is related to how database methods are created as they do not provide us with a Promise. If our database access methods also returned Promise the getTasks process would look similar to the code shown below:
Approach 3: Async/Await
With the introduction of Promises, the problem of the pyramid of doom was significantly mitigated. Even with the usage of promises, the issue of callbacks still existed. We still had to depend upon the callbacks passed to the .then and the .catch methods of a Promise.
The verifyUser method should be defined using an async function as only inside these functions; it is possible to await promises. This small change enables you to await promises without making any other changes to the other existing methods.
Benefits Of Using Async-Await:
- Error handling is far more straightforward, and it relies on try/catch similar to the other synchronous code.
- The resulting code is much cleaner.
- Debugging is much simpler. You’ll be able to step through await calls as if they were synchronous calls.
Through this blog, we have tried to anticipate how web development developers, such as yourself, can handle AJAX in 2021. While it may be a combination of primary and a few advance principals, the motive is to help the community. In case of further questions, feel free to drop in at the comments section below!
The main difference between callbacks and promises is that you tell the executing function of what to do with callbacks when the asynchronous task concludes. In contrast, in the case of promises, the executing function returns a particular object to you (the promise), and then you tell the promise what needs to be done when the asynchronous task completes.