Asynchronous programming in JavaScript allows you to execute tasks without blocking the main thread. This is essential for performing tasks like making network requests, reading files, or any other I/O operations.

Callback Functions

A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action.

  function fetchData(callback) {
    setTimeout(() => {
        console.log("Data fetched");
        callback();
    }, 1000);
}

function displayData() {
    console.log("Display data");
}

fetchData(displayData); // "Data fetched" (after 1 second) and then "Display data"
  

Promises

Promises provide a more robust way to handle asynchronous operations compared to callbacks.

  1. Creating a Promise:
  let promise = new Promise((resolve, reject) => {
    let success = true; // Change to false to test the reject path
    if (success) {
        resolve("Operation was successful");
    } else {
        reject("Operation failed");
    }
});

promise
    .then(message => {
        console.log(message); // "Operation was successful"
    })
    .catch(error => {
        console.log(error); // "Operation failed"
    });
  

Chaining Promises:

  let promise = new Promise((resolve, reject) => {
    resolve("Step 1 completed");
});

promise
    .then(message => {
        console.log(message); // "Step 1 completed"
        return "Step 2 completed";
    })
    .then(message => {
        console.log(message); // "Step 2 completed"
        return "Step 3 completed";
    })
    .then(message => {
        console.log(message); // "Step 3 completed"
    });
  

Async/Await

Async/Await is a modern way to handle asynchronous code, making it look more like synchronous code and easier to read.

  1. Using Async/Await:

      function fetchData() {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve("Data fetched");
            }, 1000);
        });
    }
    
    async function displayData() {
        let data = await fetchData();
        console.log(data); // "Data fetched" (after 1 second)
    }
    
    displayData();
      
  2. Handling Errors with Try/Catch:

      function fetchData() {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                reject("Error fetching data");
            }, 1000);
        });
    }
    
    async function displayData() {
        try {
            let data = await fetchData();
            console.log(data);
        } catch (error) {
            console.log(error); // "Error fetching data"
        }
    }
    
    displayData();
      

Using Async/Await with Multiple Promises

  1. Sequential Execution:

      async function fetchDataSequentially() {
        let data1 = await fetchData1();
        console.log(data1);
        let data2 = await fetchData2();
        console.log(data2);
    }
    
    function fetchData1() {
        return new Promise((resolve) => {
            setTimeout(() => {
                resolve("Data 1 fetched");
            }, 1000);
        });
    }
    
    function fetchData2() {
        return new Promise((resolve) => {
            setTimeout(() => {
                resolve("Data 2 fetched");
            }, 1000);
        });
    }
    
    fetchDataSequentially();
      
  2. Parallel Execution:

      async function fetchDataInParallel() {
        let [data1, data2] = await Promise.all([fetchData1(), fetchData2()]);
        console.log(data1); // "Data 1 fetched"
        console.log(data2); // "Data 2 fetched"
    }
    
    function fetchData1() {
        return new Promise((resolve) => {
            setTimeout(() => {
                resolve("Data 1 fetched");
            }, 1000);
        });
    }
    
    function fetchData2() {
        return new Promise((resolve) => {
            setTimeout(() => {
                resolve("Data 2 fetched");
            }, 1000);
        });
    }
    
    fetchDataInParallel();