From Callbacks to Promises to AsyncAwait

Single Threaded Node

In spite of having a single threaded architecture, Node is not blocked while doing parallel operations. How does Node achieve this ?
Even though Node is single threaded, it is event driven and makes use of background workers to complete the tasks. Tasks are pushed off to workers from the main thread. Main thread is not blocked and continues to listen to other requests. When the task is done, the result of the task(if any) is put back to the event loop for further processing which is accomplished by callback.
In this write up, I will be discussing on the evolution of callbacks. The way they evolved to Promises and most recently to Async-Await. Event Loop is outside the scope of this topic, for which there are plenty of articles available online.

CallBacks

As I said before, callbacks are a means by which the background workers intimate the main thread of the completion of the asynchronous task they were performing. Initially when the task was invoked, we pass a function as a parameter(callback function), which will be called after the task has been completed, and thus passing the control to the main thread.
To illustrate the concept of callback, let us consider this use case:
An user logs in to a system with his/her Email. From the Database, we get the ZipCode associated with that email. Now using the ZipCode, we get the list of pickups from a 3rd party service. And finally we call another service with the list of pickups, to get the recommended pickups.
Email -> ZipCode -> PickUps -> RecommendedPickUps
We have 3 functions:
  1. getZipCodeUsingEmail- takes Email as a parameter and returns ZipCode
  2. getPickupsUsingZipCode- takes ZipCode as a parameter and returns Pickups
  3. getRecommendedPickups- takes Pickups as a parameter and returns RecommendedPickups
In our case, the functions call “setTimeout” function inside to simulate asynchronous Database/Webservice calls. Each function takes 2 input parameters. One is the input data parameter and the other is the callback function parameter. The callback function parameter(“cb”) is a function that has 2 parameters: Error parameter and output data parameter.

Usual format of a callback function


The 3 functions:

First function. Email is first input parameter. Returns ZipCode.
2nd function. ZipCode obtained from first function is first input parameter. Returns Pickups.
3rd function. Pickups obtained from 2nd function is the first input parameter. Returns RecommendedPickups.


And this is how we invoke the functions:


Note that we pass functions as input parameters in place of the callback parameters. This will work, but as you can see the code gets really ugly and it turns out into what we is usually called as Callback hell or Christmas Tree code. It becomes difficult to manage. Arrow functions will make the code less verbose, but still the readability is very less. Enter Promises!

Promises

Promises were introduced such that an abstraction was created over the callbacks. Instead of waiting for the callback method to be called, the asynchronous function will immediately return an object(promise), that will resolve to the result from the function or get rejected in case of an error.
The functions that we have needs to be slightly modified, such that the callback parameter is omitted and the function returns a Promise object.


Functions that return Promises instead of calling callback functions



And this is how we invoke the functions:



Bonus: (Invocation involving Arrow functions)


Notice the increase in readability and how the execution feels synchronous. This simplification is what Promise provides us with.

Async-Await

The most recent improvement in handling asynchronous functions is the introduction of Async-Await. Code is even less verbose and feels more synchronous than Promises. Functions need to be declared as “async” and while executing the function, function needs to be awaited for the results to be returned.

Async version of the Functions


Note that an async function can be awaited only inside an async function. Hence the new async method “DoWork”. Also note that the result of an async function is a Promise, which will resolve to the actual result.


Conclusion

Without a doubt, Async/Await usage is the simplest and most readable, of the 3 styles. So any Asynchronous code that we write from now on, should be implemented using AsyncAwait. And any code that we may have written previously in the other 2 forms, can be rewritten easily to the AsynAwait style with the help of libraries like Promisify.

Comments

Popular posts from this blog

My First Full Marathon

My Second Full Marathon

ASP.Net Core saves me $4 a month!