# Asynchronous Programming in Javascript

Here are some examples of asynchronous programming in JavaScript using **callbacks**, **Promises**, and **async/await**.

### 1\. Using Callbacks

A callback is a function passed as an argument to another function, which is then executed after some operation completes. This approach was common in early JavaScript for handling asynchronous tasks, like data fetching.

**Example: Fetching data with a callback**

```javascript
function fetchData(callback) {
  setTimeout(() => {
    const data = "Some fetched data";
    callback(data);
  }, 2000); // simulating network delay
}

fetchData((data) => {
  console.log("Data received:", data);
});
```

In this example, `fetchData` takes a callback function as an argument. After a delay, it calls the callback function with the fetched data.

### 2\. Using Promises

Promises provide a cleaner way to handle asynchronous operations by avoiding "callback hell" (deeply nested callbacks). They have three states: **pending**, **fulfilled**, and **rejected**.

**Example: Fetching data with a Promise**

```javascript
function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const data = "Some fetched data";
      resolve(data); // fulfill the promise
    }, 2000);
  });
}

fetchData()
  .then((data) => {
    console.log("Data received:", data);
  })
  .catch((error) => {
    console.error("Error:", error);
  });
```

In this example, `fetchData` returns a Promise. We use `.then()` to handle the data when the promise is fulfilled, and `.catch()` to handle any errors if the promise is rejected.

### 3\. Using async/await

`async/await` syntax, introduced in ES8, makes asynchronous code look and behave more like synchronous code, improving readability. It works on top of Promises and allows you to "await" the completion of a promise within an `async` function.

**Example: Fetching data with async/await**

```javascript
function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const data = "Some fetched data";
      resolve(data);
    }, 2000);
  });
}

async function getData() {
  try {
    const data = await fetchData(); // wait for the promise to resolve
    console.log("Data received:", data);
  } catch (error) {
    console.error("Error:", error);
  }
}

getData();
```

In this example, the `getData` function is marked as `async`, allowing us to use `await` to pause the function until `fetchData` resolves. This results in a more readable and manageable way to handle asynchronous code.

### 4\. Real-World Example: Fetching Data from an API

Here’s how you might fetch data from an API using `fetch` with async/await:

```javascript
async function fetchFromAPI() {
  try {
    const response = await fetch("https://jsonplaceholder.typicode.com/posts/1");
    if (!response.ok) {
      throw new Error("Network response was not ok");
    }
    const data = await response.json();
    console.log("Data from API:", data);
  } catch (error) {
    console.error("Fetch error:", error);
  }
}

fetchFromAPI();
```

In this example:

1. We use `await fetch(url)` to wait for the API call to complete.
    
2. We check if the response is okay (`response.ok`). If not, we throw an error.
    
3. If successful, we parse the response data with `.json()` and log it to the console.
