What is Asynchronous JavaScript

Callbacks vs. Promises vs. Async/Await in JavaScript

Bhagya Vithana
Enlear Academy

--

JavaScript is a single-threaded programming language which means only a single process can happen at a time.

Normally in a single-threaded language, there is nothing to worry about concurrency issues since it simplifies coding. But at the same time, without blocking the main thread, you can’t perform long operations like network access.

Let me explain this through a real-world scenario.

Assume requesting an API for some data. The server can take some time to process the request while blocking the main thread, which causes the web page to become unresponsive. That’s where asynchronous JavaScript comes into the picture.

You can perform long network requests without blocking the main thread using asynchronous JavaScript (callbacks, promises, and async/await).

You can run multiple tasks concurrently by asynchronous execution of tasks, even in a single thread.

What is Blocking in JavaScript?

Photo by Will Porada on Unsplash

In the asynchronous execution of programming, multiple tasks will be handled concurrently once the outcome of each task is accessible. Thus, by using asynchronous code, applications with CPU-intensive and heavy database queries can be resolved.

For example, if we have to do some heavy lifting with costly database queries and make an ajax request, execution needs to wait until & unless the next lines of code have received some feedback from the DB server. So, naturally, this makes the application less responsive.

I think that you’re probably aware that standard Ajax requests don’t complete synchronously. So let’s go with a small example. We used an Ajax request just as an example.

ajax('https://sampleproblem.com/api', function(res) {    console.log(res); // `res` is now available});

Although you can make synchronous Ajax requests, never go for that solution. If you make a synchronous Ajax request, it will mostly block your UI.

Normally, asynchronous tasks are handled in other languages by creating a new thread that runs in the background. However, we have something in JavaScript called the execution context and event loop, which block the rest of the code.

When execution context finds out such code blocks, it sends them to the event loop and returns them to the call stack after execution.

How to Write Asynchronous Functions in JavaScript?

There are three main types of asynchronous code styles in JavaScript code. They are callbacks, promises, and async/await.

1. Callback Functions

Photo by Quino Al on Unsplash

As you already understand, callbacks are the most popular way to express and handle asynchronous programs in JavaScript programs.

A callback is a function to be executed after the execution of another function is completed. The callback function is called with the response when the action ends.

Since callbacks are a way to ensure a certain code doesn’t execute until the other code has already finished execution, it can be easily achieved using a simple callback function.

To understand what I’ve explained above, let me start with a simple explanation. Assume that we want to log a message to the console, but it should be there after 5 seconds. The following code snippet will explain this scenario.

const message = function() {  
console.log("The response is shown after 5 seconds");
}

setTimeout(message, 5000);

A built-in JavaScript method called “setTimeout,” calls a function or evaluates an expression after a given period. Here, the message function is called after a particular period (it is 5 seconds).

Though Callback provides a simple mechanism to handle asynchrony in JavaScript, there are some drawbacks associated with callback functions.

The challenges with callbacks:

  • If not used correctly, it is simple to create callback hells with them.
  • Error handling is difficult.
  • Can’t return values with the return statement, nor can use the throw keyword.

What is a “Callback Hell”?

There is a chance for every callback chain to become a callback hell. It becomes difficult to handle and comprehend when the amount of nesting rises in callbacks.

As a result, the code gets deeper and more difficult to handle as calls become nested.

This specific callback framework is referred to as “callback hell” or “pyramid of doom.” It makes things more difficult to read because they can go very far.

firstFunction(args, function() {
secondFunction(args, function() {
thirdFunction(args, function() {
// And so on…
});
});
});

Here we have a sequence of three functions, each representing a move in an asynchronous series, nested together. This kind of code is often called a “callback hell.” Fortunately, there are other ways of stopping pyramids like that. One of the best strategies is to make use of “promises.”

2. Promises

Photo by Kelly Sikkema on Unsplash

A promise represents the eventual result of an asynchronous operation.

It enables you to connect handlers with the eventual success value or cause of failure of an asynchronous action. This allows asynchronous methods to act like synchronous methods.

Instead of returning the final value instantly, the asynchronous approach returns a promise at some stage to supply the value in the future. It can be in three states, fulfilled, pending, or rejected.

  • Pending: This is the initial state of the Promise before an operation begins
  • Fulfilled: This means the particular operation has completed its execution.
  • Rejected: The operation did not complete (error value is thrown).

Let’s move on to a simple example that explains a basic promise structure.

Something.save()  
.then(function() {
console.log('initial saving is a success');
})
.catch(function() {
//error handling
})

Both the then can be invoked with the asynchronous operation, and then catch invokes the error handling and returns why it could not be fulfilled.
Another great thing about Promises is that they can be chained. To understand this concept, let’s go through the following coding snippet given below.

saveSomething()   
.then(updateOrders)
.then(AddOrders)
.then(results);

With Promises, the then(…) call can take two functions, the first for fulfillment (as shown earlier) and the second for rejection. Most of the latest libraries out there support both callbacks and a Promises interface at the same time.

Benefits of Promises

  1. Comparing to callback code, readability is significantly high.
  2. Better handling of asynchronous operations (can synchronously handle asynchronous operations).
  3. Error Handling is easy.

3. Async / await? (asynchronous/await)

Photo by Artem Maltsev on Unsplash

Along with ES8 , async/await was introduced, which makes the job of working with Promises easier. So, let’s see how async/await works.

The purpose of async/await is to optimize the nature of promises. The async and await keywords allow for a simplified style of asynchronous, promise-based behavior in the manner of synchronous code execution.

Async functions always return a promise. If not, it will be implicitly wrapped in a promise. An async function can contain an await expression, that pauses the function's execution and waits for the passed Promise’s resolution, then resumes the async function’s execution and returns the resolved value. Let’s take a look at the following example:

async function save(output) {  
try {
await output.save()
}
catch (ex) {
//error handling
}
console.log('success');
}

As you can see in this case, you have to put the async keyword before the actual function declaration. After that, inside your newly created async function, the await keyword can be used.

Async functions contain zero or more await expressions. The await keyword is only valid in async functions. It allows you to wait until that promise resolves and results, whether successful or rejected(data or error).

Conclusion

So, to be an amazing JavaScript developer, we learned how asynchronous JavaScript works and other concepts. That’s it, and please press the clap button if you find this article helpful.

--

--

Software Engineer| Technical Writer| University of Moratuwa| Faculty of Information Technology