DEV Community

Mukesh
Mukesh

Posted on

Mastering Asynchronous JavaScript: Promises, Async/Await, and Callbacks

Many operations in the real world, such as retrieving data from a server or reading a file, take time to finish. If these operations were synchronous, then we need to wait until the entire task is completed, resulting in a bad user experience. Asynchronous operations enable the program to continue executing other tasks while these operations are complete.

1. Understanding Callbacks

Callbacks are functions passed as arguments to other functions, which are then invoked when the task is complete.

// Example of a callback function
function fetchData(callback) {
    setTimeout(() => {
        callback("Data fetched from server");
    }, 2000);
}

// Using the callback
fetchData((message) => {
    console.log(message); // Outputs: Data fetched!
});
Enter fullscreen mode Exit fullscreen mode

Code Explanation

Function Definition:

The function fetchData is defined to accept one argument, callback.
callback is expected to be a function that will be called once the asynchronous operation inside fetchData is complete.

setTimeout:

Inside fetchData, there is a call to setTimeout.
setTimeout is a built-in JavaScript function that executes a function after a specified delay in milliseconds.
it is used to simulate a delay of 2000 milliseconds (2 seconds) to mimic an asynchronous operation like fetching data from a server.

Callback Invocation:

After the 2-second delay, the anonymous function passed to setTimeout is executed.
This function calls callback with the argument "Data fetched from server".
This means after the delay, the callback function will be executed with the provided message.

Calling fetchData:

fetchData is called with an anonymous function as its argument. This anonymous function takes one parameter, message.
The anonymous function is the callback function that will be executed once the data is "fetched" (after the 2-second delay).

Callback Execution:

After 2 seconds, setTimeout triggers the execution of the callback with the message "Data fetched from server".
The callback function logs the message to the console.

2. Working with Promises

A promise is an object representing the eventual completion or failure of an asynchronous call. It provides methods to handle asynchronous results and handle errors. It provide a consistent way to handle errors using .catch().

function fetchData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("Data fetched from server");
        }, 2000);
    });
}

fetchData()
    .then((message) => {
        console.log(message); // Outputs: Data fetched!
    })
    .catch((error) => {
        console.error(error);
    });

Enter fullscreen mode Exit fullscreen mode

Code Explanation

Function Definition:

The fetchData function is defined to return a new promise.
A promise is an object that represents an asynchronous operation that will eventually complete or fail.

Creating the Promise:

Inside fetchData, a new promise is created using new Promise((resolve, reject) => {...}).
The promise constructor takes a function with two parameters, resolve and reject. These are functions that you call when the asynchronous operation completes successfully (resolve) or fails (reject).

Simulating an Asynchronous Operation:

The setTimeout function is used to simulate a delay of 2000 milliseconds (2 seconds), representing an asynchronous operation like fetching data from a server.
After the 2-second delay, the resolve function is called with the message "Data fetched from server".
This means the promise is successfully fulfilled after 2 seconds with the specified message.

Calling fetchData:

The fetchData function is called, which returns a promise.

Handling the Promise:

The .then() method is chained to the promise returned by fetchData.
The function passed to .then() is called when the promise is resolved successfully. It receives the message "Data fetched from server" and logs it to the console.

Error Handling:

The .catch() method is chained to handle any errors if the promise is rejected.
If there were an error (for example, if reject were called inside the promise), the function passed to .catch() would be called to log the error.

3. Async/Await: Modern Asynchronous JavaScript

Async and Await are powerful keywords used for handling asynchronous operations using promises. Async functions returns a promise and await pauses the execution until Promise is resolved. It simplify asynchronous code and improve readability by making it appear synchronous.

async function fetchData() {
    try {
        let message = await new Promise((resolve) => {
            setTimeout(() => {
                resolve("Data fetched from server");
            }, 2000);
        });
        console.log(message); // Outputs: Data fetched!
    } catch (error) {
        console.error(error);
    }
}

fetchData();

Enter fullscreen mode Exit fullscreen mode

Code Explanation

Async Function:

The fetchData function is declared with the async keyword. This means it can use await inside it and it returns a promise.

Try-Catch Block:

The try block contains code that might throw an error. If an error occurs, the control moves to the catch block to handle the error.
The catch block catches and handles any errors that occur inside the try block.

Awaiting a Promise:

Inside the try block, we have an await expression. It waits for the promise to resolve before moving to the next line of code.
The promise is created using new Promise((resolve) => {...}).
The setTimeout function is used to simulate a delay of 2000 milliseconds (2 seconds), representing an asynchronous operation like fetching data from a server.
After 2 seconds, the promise is resolved with the message "Data fetched from server", and this message is assigned to the message variable.

Logging the Message:

After the promise is resolved, the message is logged to the console using console.log(message).

Error Handling:

If an error occurs while waiting for the promise or during any operation in the try block, the control moves to the catch block.
The error is logged to the console using console.error(error).

Calling the Async Function:

The fetchData function is called. Since it's an async function, it runs asynchronously.

Comparison Summary

Callbacks are simple and direct but can lead to messy code and inconsistent error handling.

Promises offer a more structured way to handle asynchronous operations and provide better error handling.

Async/Await builds on promises to provide even cleaner, more readable, and maintainable asynchronous code.

Top comments (2)

Collapse
 
vsh_torch profile image
Vengatesh TR

Thanks for sharing. Nice explanation

Collapse
 
mukeshb profile image
Mukesh

thanks