DEV Community

Cover image for Asynchronous Operations in JavaScript

Posted on

Asynchronous Operations in JavaScript

JavaScript is synchronous by default, which means operations execute from top to bottom. Synchronous operations in JavaScript requires having each step of an operation wait for the previous step to execute completely. This means no matter how long the previous process takes, subsequent process won't start off until the prior is completed.

Asynchronous means that things can happen independently of the main program flow.
In Asynchronous operations, any process that takes a lot of time to process is usually run alongside other synchronous operation and completes in the future.
Let us see the fundamental concepts that JavaScript relies on to handle asynchronous operations.

These concepts include:
Callback functions,
Promises and
Async and Await

For instance:

console.log("Get up Early");
console.log("Express gratitude for what you have");
console.log("Do something productive and fun");

If we run the code above, we have the following logged in the console:

Get up Early
Express gratitude for what you have
Do something productive and fun

Now let's change that a bit so that 'Express gratitude for what you have' takes longer than 'Do something productive and fun' :

console.log("Get up Early");

setTimeout(function() {
console.log("Express gratitude for what you have")

console.log("Do something productive and fun");

We get the following in the console:

Get up Early
Do something productive and fun
Express gratitude for what you have

The setTimeout function makes the operation asynchronous by delaying "Express gratitude for what you have" to occur after 3 seconds. The whole operation doesn’t pause for 3 seconds so it can log “Do something productive and fun”.

CallBack Functions

A callback is a simple function that's passed as a value to another function, and will only be executed when the event happens. We can do this because JavaScript has first-class functions, which can be assigned to variables and passed around to other functions (called higher-order functions).
Using callback functions is a core functional programming concept, and you can find them in most JavaScript code; either in simple functions like setInterval, event listening or when making API calls.
The callback function is not run unless called by its containing function. Hence, the term call back function.

One common example of callback functions:

setTimeout(() => {
// runs after 2 seconds
}, 2000)

Problem with callbacks

Callbacks are great for simple cases and are useful for short asynchronous operations. When working with large sets, this is not considered best practice.
Every callback adds a level of nesting, and when you have lots of callbacks, the code starts to become complicated very quickly and sometimes the code becomes incomprehensible and is not easily refactored. This is known as callback hell.

Alternatives to callbacks

Starting with ES6, JavaScript introduced several features that help us with asynchronous code that do not involve using callbacks:
1.Promises (ES6)
2.Async/Await (ES8)


Promises are one way to deal with asynchronous code, without writing too many callbacks in your code.
1.Introduced in ES6.
2.Superseded in 2018, by async functions
Async Functions use the promises API as their building block.

Working of Promises

Promises have 3 states:
1.Pending State
2.Fulfilled/Resolved State
3.Rejected State

When the promise has been called, it will start in the Pending State.This means that the caller function continues the execution, while it waits for the promise to do its own processing, and give the caller function some feedback.
The caller function now waits for it to either return the promise in the resolved state or in the rejected state.
If it returns in the resolved state, this means that the specified operation was completed, but if it returns in the rejected state, it means the operation did not complete and an error value is thrown.

How Promises are Created??

The Promise object is created using the new keyword and contains the promise; this is an executor function which has a resolve and a reject callback. Using resolve and reject helps us to communicate back a value.

For instance:
const promise = new Promise(function(resolve, reject) {
// promise description

Using Promises

Once a promise has been created, using it is pretty straightforward and simple. We can use them by chaining .then() and .catch().

Async and Await

JavaScript is even simpler with the async/await syntax.
Async functions are a combination of promises and generators, and basically, they are a higher level abstraction over promises. Let me repeat: async/await is built on promises.
They reduce the boilerplate around promises, and the "don't break the chain" limitation of chaining promises.
Promises were introduced to solve the famous callback hell problem, but they introduced complexity on their own, and syntax complexity.
Async functions make the code look like it's synchronous, but it's asynchronous and non-blocking behind the scenes.

Top comments (0)