jsnotes

Topic 002: Closure, Callback, Async/Await, Promises

Topic 002: Closure, Callback, Async/Await, Promises

Sure! Let’s go through each concept with some JavaScript code examples.

1. Closure:

Closure is a fundamental concept in JavaScript that allows functions to retain access to variables from their containing scope even after the parent function has finished executing. This means that inner functions have access to the variables and parameters of their outer function, even after the outer function has returned.

function outerFunction() {
  let outerVar = "I am outer";

  function innerFunction() {
    console.log(outerVar);
  }

  return innerFunction;
}

let closureExample = outerFunction();
closureExample(); // Output: I am outer

In this example, innerFunction retains access to the outerVar variable even after outerFunction has finished executing. This is possible because of closure.

2. Callbacks:

Callbacks are functions that are passed as arguments to another function and are executed after the completion of that function. They are commonly used in asynchronous programming to handle asynchronous tasks.

function fetchData(callback) {
  setTimeout(() => {
    callback("Data fetched");
  }, 2000);
}

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

fetchData(displayData); // Output (after 2 seconds): Data fetched

In this example, fetchData is an asynchronous function that simulates fetching data after 2 seconds. The displayData function is passed as a callback to fetchData, and it gets executed with the fetched data after the asynchronous task completes.

3. Promises:

Promises are objects representing the eventual completion or failure of an asynchronous operation. They allow you to handle asynchronous operations more elegantly than callbacks.

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

fetchData()
  .then((data) => console.log(data)) // Output (after 2 seconds): Data fetched
  .catch((error) => console.error(error));

In this example, fetchData returns a Promise that resolves with the fetched data after 2 seconds. We use the then method to handle the successful resolution of the Promise and the catch method to handle any errors that might occur.

1. Explanation with Code:

A Promise in JavaScript represents a future value or an asynchronous operation that will eventually produce a value or result. It can be in one of the three states: pending, fulfilled, or rejected.

Here’s an example of a Promise in JavaScript:

// Creating a promise
const myPromise = new Promise((resolve, reject) => {
  // Asynchronous operation, e.g., fetching data
  setTimeout(() => {
    const data = 42;
    if (data) {
      resolve(data); // If successful, call resolve with the result
    } else {
      reject(new Error("Data not found")); // If failed, call reject with an error
    }
  }, 2000); // Simulating a delay of 2 seconds
});

// Consuming the promise
myPromise
  .then((result) => {
    console.log("Promise resolved with result:", result);
  })
  .catch((error) => {
    console.error("Promise rejected with error:", error);
  });

In this code:

2. Creating a Custom Promise:

We can also create custom promises for our specific needs. Here’s an example:

function customPromise(condition) {
  return new Promise((resolve, reject) => {
    if (condition) {
      resolve("Custom promise resolved");
    } else {
      reject(new Error("Custom promise rejected"));
    }
  });
}

// Consuming the custom promise
customPromise(true)
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
    console.error(error);
  });

In this code:

Promise.all()

const axios = require("axios");

async function fetchData() {
  try {
    const [response1, response2, response3] = await Promise.all([
      axios.get("https://api.endpoint1.com"),
      axios.get("https://api.endpoint2.com"),
      axios.get("https://api.endpoint3.com"),
    ]);

    // Handle responses here
    console.log(response1.data);
    console.log(response2.data);
    console.log(response3.data);
  } catch (error) {
    console.error("Error fetching data:", error);
  }
}

fetchData();

This code will make three API calls concurrently using Axios (you can use any HTTP client library), and once all three calls are complete, it will handle the responses accordingly.

4. Async/Await:

Async/await is a syntactic sugar built on top of Promises, providing a more readable and synchronous-like way to write asynchronous code.

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

async function displayData() {
  try {
    const data = await fetchData();
    console.log(data); // Output (after 2 seconds): Data fetched
  } catch (error) {
    console.error(error);
  }
}

displayData();

In this example, fetchData returns a Promise as before. However, with async/await, we can use the await keyword to pause the execution of displayData until the Promise returned by fetchData is resolved or rejected. This results in code that looks more synchronous and is easier to understand.