Async, Sync , Await and Promises in Javascript

Gaurav SharmaGaurav Sharma
6 min read

Synchronous vs. Asynchronous Functions:

Synchronous Function:

A synchronous function follows a sequential process, where each step happens one after the other, in a fixed order. Let's say we are preparing a banana shake. First, we take the jar, then go to the market to buy bananas. After that, we cut the bananas and put them into the jar. Then we add some milk, go back to the market to buy sugar and chocolate syrup, and finally, we blend everything together. In this synchronous process, every step is executed one at a time, meaning the next step doesn't start until the previous one is completed.

Asynchronous Function:

An asynchronous function delegates certain tasks to others, allowing multiple tasks to happen simultaneously, thus saving time. For example, while preparing the banana shake, instead of going to the market yourself to buy bananas, you assign that task to someone else. Similarly, you delegate buying sugar and chocolate syrup to another person. Meanwhile, you can continue cutting the bananas and adding milk to the jar. By the time you’re ready to blend the shake, the other people have returned with the items you need. This asynchronous process takes less time overall because tasks are performed concurrently.

Conclusion:

Asynchronous functions are generally faster because they allow multiple tasks to be done simultaneously by delegating work, unlike synchronous functions where each task must be completed before the next one can start.

// Function to simulate going to the market and buying stuff
function goToMarket(item, callback) {
    console.log(`Going to the market to buy ${item}...`);
    console.log(`Bought ${item}.`);
    if (callback) callback(); // Call the callback after buying the item
}

// Function to make the banana shake
function makeBananaShake() {
    console.log("Step 1: Take the jar.");

    // Go to the market and buy bananas
    goToMarket('bananas', () => {
        console.log("Step 2: Cut the bananas and put them into the jar.");

        console.log("Step 3: Add milk to the jar.");

        // Go to the market and buy sugar
        goToMarket('sugar', () => {

            // Go to the market and buy chocolate syrup
            goToMarket('chocolate syrup', () => {

                console.log("Step 4: Add sugar and chocolate syrup to the jar.");

                console.log("Step 5: Blend everything together.");

                console.log("Banana shake is ready!");
            });
        });
    });
}

// Call the function to make the banana shake
makeBananaShake();

Async Implementation in JS.

// Function to simulate making a banana shake
function makeBananaShake() {
    console.log("Step 1: Take the jar.");
    console.log("Step 2: Cut the bananas and put them into the jar.");
    console.log("Step 3: Add milk to the jar.");
    console.log("Step 4: Add sugar and chocolate syrup to the jar.");
    console.log("Step 5: Blend everything together.");
    console.log("Banana shake is ready!");
}

// Function to simulate going to the market to buy bananas
function goToMarketToBuyBananas() {
    console.log("Going to the market to buy bananas...");
    console.log("Bought bananas!");
    makeBananaShake(); // After buying bananas, we proceed to make the shake
}

// Simulate asynchronous behavior using setTimeout
setTimeout(goToMarketToBuyBananas, 2000);

console.log("Hello, World!");  // This will be logged immediately

Promises in Javascript

A promise is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value. Promises are used to handle asynchronous operations in a more manageable and clean way than traditional callback-based approaches.

3 States in Promise

  1. Pending

  2. Resolved.

  3. Rejected

3 Methods to handle the Promises

  1. then -: Used to handle the fulfillment of promise

  2. catch -Used to handle errors or rejections of a promise

  3. finally -: Executes code after the promise is settled, regardless of its result (fulfilled or rejected).

// Creating a promise
let myPromise = new Promise((resolve, reject) => {
    let success = true;

    if (success) {
        resolve("The operation was successful!");
    } else {
        reject("The operation failed.");
    }
});

// Handling the promise
myPromise
    .then((message) => {
        console.log(message); // Logs: The operation was successful!
    })
    .catch((error) => {
        console.log(error); // This would log if the promise was rejected
    })
    .finally(() => {
        console.log("Promise settled."); // Always runs whether fulfilled or rejected
    });

Async and Await -:

To handle asynchronous operations more elegantly, we use async and await. The await keyword can only be used inside an async function. In other words, any function that intends to use await must be declared with async. Without async, the use of await is not possible.

// Simulating going to the market to buy bananas (an asynchronous operation)
async function goToMarket() {
    console.log("Going to the market...");
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve("Bought bananas!");
        }, 2000); // Simulating a 2 second delay
    });
}

// Simulating making the banana shake
async function makeBananaShake() {
    console.log("Starting the banana shake process...");

    // Await the result of goToMarket()
    const marketResult = await goToMarket();
    console.log(marketResult);  // This will run after 2 seconds

    console.log("Step 1: Take the jar.");
    console.log("Step 2: Cut the bananas and put them into the jar.");
    console.log("Step 3: Add milk to the jar.");
    console.log("Step 4: Add sugar and chocolate syrup to the jar.");
    console.log("Step 5: Blend everything together.");
    console.log("Banana shake is ready!");
}

// Calling the async function
makeBananaShake();

Simple Example to Understand Async and await.

Making a Coffee -:

Imagine you are making coffee, but you need to heat the water first. Heating the water takes some time (asynchronous task). Once the water is hot, you can then make the coffee.

// Function to simulate heating water (takes 2 seconds)
async function heatWater() {
    console.log("Heating water...");
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve("Water is hot!");
        }, 2000); // Simulating a 2-second delay
    });
}

// Function to make coffee
async function makeCoffee() {
    console.log("Starting coffee making process...");

    // Wait for the water to be heated
    const hotWater = await heatWater();
    console.log(hotWater); // Logs "Water is hot!" after 2 seconds

    console.log("Step 2: Add coffee to the hot water.");
    console.log("Step 3: Stir and enjoy your coffee!");
}

// Call the async function
makeCoffee();
  • async function heatWater():

    • This function simulates heating water, which takes 2 seconds. It returns a promise that resolves after 2 seconds with the message "Water is hot!".
  • async function makeCoffee():

    • This function uses await to pause execution until the water is heated (i.e., until the promise returned by heatWater() is resolved).

    • Once the water is hot, it logs the next steps for making coffee.

How async and await Work:

  • async: Declares that the function will perform asynchronous operations and always returns a promise.

  • await: Pauses the execution of the function until the promise resolves (or rejects). It makes asynchronous code look more like synchronous code, improving readability.

In this example, await waits for the water to finish heating before proceeding to make coffee. Without await, the function would not pause, and the coffee-making steps would execute immediately without waiting for the water to heat up.

1
Subscribe to my newsletter

Read articles from Gaurav Sharma directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Gaurav Sharma
Gaurav Sharma