Expedition of Async Programming in JavaScript

Shravan Kumar B
OhMyScript
6 min readApr 18, 2020

--

This is a story of my expedition of understanding Asynchronous Programming in JS. Coming from the Computer Science background, I got various opportunities to explore various platforms, where I could learn different programming languages, from the Assembly languages to High-level languages. But, never have I struggled to understand the flow of the language.

As much as I have known, JS is one of those technologies that outstand the rest that is in the industry. Far more, I am not speaking about performance, optimization, and things so on.

JS in Node is the asynchronous programming language.
This might be pretty confusing. To put it up in simple words,

JavaScript is a single threaded language; which means it has a one call stack and memory heap, that simply executes the code in the order it is written. It must finish executing the a peice of code before it moves to the next.

This is synchronous behavior of JS.

Whoa! You might wonder, then why the title async programming and what on Earth does Async Programming have to do with JavaScript?

For this, we should thank, Ryan Dahl, the master criminal behind one of the most dominant, powerful runtime environment Node.js.
Node.js was an environment, which enabled Asynchronism in Javascript.

Asynchronism in JavaScript , or now on , let’s say the Node.js, runs in a single process, without creating another thread.
This simply means that unlike other languages like Java, Python, which block their thread for I/O ( What this means is, when we have to do an API call, accessing the database or reading text files, so on) will pause the runtime, until this process is completed.

In Node.js, it is totally contrary to the above situation. Node.js uses something referred to as Callbacks.

Simple Real life scenario, let us say,
You are cooking and have to wash clothes as well. You put the clothes into washing machines for a wash, and resume back to cooking. Once the washing is done, you can get back to it.
Washing clothes did not stop you, from cooking? Did it?
The same principle applies here as well.

I suppose this gives you basic ideology behind What and How of Async Programming, and now let us dive into something deeper, What and How of Async Programming in Node.js.

Async Programming in Node.js

In Node.js, functions that are operating the asynchronous operations, on completing the execution of the asynchronous task, they control return and pushed onto a stack known to us as a callback.

Callback

This is how I initially understood, that the way Node.js works using a callback.
For instance,

function getUserById(userId, function(err, result){
if(err)
throw err;
console.log(result);
});
console.log('Hello World');OUTPUT:
Hello World
{ id:1, name: Lee }

The above function executes and the corresponding result of it, which is available inside the callback.

In Node.js, for it to know, that a function has some asynchronous operation, i.e., returns some data or throws some error, points to a function that will be executed once the asynchronous operation is done.

This function which deals with returned value/throw error is called a Callback function.

Meanwhile, Node.js ensures it continues with the normal execution of the code; just as the above example, where console.log() did not wait for the complete execution of the getUserById().

Haha!!

Callback hell, the vexation

As time passed, I found it quite frustrating to deal with these Callback functions.

The first problem being, variable scope and the second dealing with too many callbacks to access the results of the callback function; as we cannot return the resultant value from the callback.

function getUserById(userId, function(err, result){
if(err)
throw err;
console.log(result);
});
console.log(result);
console.log('Hello World');
OUTPUT:
undefined
Hello World
{ id:1, name: Lee, userEmail:iam@you.com}

The only way to access or do with anything with the result was to operate inside the callback.

function getUserById(userId, function(err, result){
if(err)
throw err;

function sendEmail(result.userEmail, function(err,result){
// doSomething();// maybe check if mail is sent
});
});

This caused me a lot of confusion with the naming a variable, accessing the variable. All in the flow of the code and function calls. This is where I realized, I was dealing with something called CALLBACK HELL because of its confusing paradigm view.

Heartily thankful to StackOverflow.

Promises

This is where I found a solution to my problem of Callback Hell; something called Promises.
A Promise is an object that encapsulates an asynchronous operation and on completion of the execution. It is a proxy for a value not known when the asynchronous code starts executing.

Sounds very similar to Callback?
It is quite similar to Callback design but was lesser sophisticated in terms of readability of the code, variable scope, and variable hoisting.

Instead of giving a callback function to deal with the asynchronous function, Promises API provides its own methods called “.then” and “.catch”, which basically executes based on functional chaining, as given below.
The same example, in terms of promises;

getUserById(userId)
.then(function(result){
console.log(result);
return result;
})
.then(function(userData){
console.log('Email to be sent');
sendEmail(userData.userEmail);
console.log('Email sent');
})
.catch(function(err){
throw err;
});
console.log('Hello World');
console.log(result);
OUTPUT:
Hello World
undefined
{id:1, name:someone, userEmail:someone@everyone@email.com }
Email to be sent
Email sent

This looks basically more organized than the way how Callback looked and the way code structure seemed in the callback functions.

This paradigm too had issues. This dealt with firstly what I call as Promise Hell when you need to keep passing the resultant to, too many ‘then’ functions.

Another major setback was the scope again. Data flow can be passed/ returned from one then() to another then(), but were not available outside for processing.

LMAO!!

This became a very major issue when I had to deal with several complex API’s and that’s when again I came across the solution, again in the StackOverflow, which was something called Async/Await.

Async/Await

This was a step forward for me, to write several logics simplified and sorted.

When a JavaScript date has gone bad, “Don’t call me, I’ll callback you. I promise!”

It turned out to be the ultimate solution for several problems I had been facing during the development.

Async/Await is one best way to make asynchronous Node.js operate more imperatively.

This was one such pattern, where asynchronous operations were handled much more simplified way.
It brings in two keywords into the picture;
async” and “await”, where async is for declaring a function that will be dealing with some asynchronous operations and await is used to declare inside the “async” function that it shall “await” for the result of the asynchronous operation.

Simple Example with the above code,

async someFunction(userId){
try{
let user = await getUserById(userId);
console.log(user);
console.log('Email to be sent');
await sendEmail(userData.userEmail);
console.log('Email sent');
console.log('Hello World');
}
catch(error){
throw error;
}
}
OUTPUT:
{id:1, name:someone, userEmail:someone@everyone@email.com }
Email to be sent
Email sent
Hello World

In the above instance, isn't the code more readable? The code is neatly structured and also makes sense with the flow and how it executes. Async/Await makes the code look more like a synchronous code format.

One major thing to be considered in async/await way of handling asynchronous operation is that a function call can await only its “awaitable”.

Awaitable, meaning a function that returns a Promise Object or if its function carrying some asynchronous operation.

This was my journey of learning the Asynchronous programming with Node.js.

Conclusion

From my point of view, I believe Callbacks are one of the worst way to code and deal with asynchronous operations given at any point in time. But the Promise method of dealing with asynchronous programming overcomes with Callback Hell issue. But definitely see that Promises methodology of dealing with async operations has its own perks in different cases. Async/Await might seem the best solution to be as of now. But don't limit yourself to these. Explore and use each of them based on the optimal use cases that you are dealing with.

Thanks for reading. Signing off, until next time.

HAPPY LEARNING

--

--

Shravan Kumar B
OhMyScript

A caffeine-dependent life-form yearning about Technology | Polyglot Developer | Open Source Lover