My Learning about Promise, async/await, and Promise.all
Tags: beginners, javascript, promise
Published: 2023-02-23 23:59:31

I felt that I lacked an in-depth understanding of Asynchronous when I created the app fetching data from external API so I revised it and I integrate my learning here. I heard we use async/await and Promise.all very often in the real industry. I am going to introduce that topic.
## Promise
Promise is an object through which you could do some async operations.
Promise has three states.
1. Fulfilled State - Success State
2. Rejected State - Failure/Reject State
3. Pending State - Waiting for response

```javascript
let myPromise = new Promise((resolve, reject) => {
  resolve("Hi I was resolved!"); // Fulfilled State
  reject("HI i was rejected"); // Rejected State
});

myPromise;

```
We can use the `.then()` method to specify what should happen when the Promise is resolved, and the `.catch()` method to specify what should happen when the Promise is rejected. In addition, we can also use the `.finally()` method to specify a callback that should be executed regardless of whether the Promise was resolved or rejected.

```javascript
let amount = 10000;

let transaction = new Promise((resolve, reject) => {
  console.log("Trying transaction, for downpayment of amount $8000");

  if (amount - 8000 > 0) {
    resolve({
      message: "Successfully Approved!",
      balance: amount - 8000,
    });
  } else {
    reject({
      message: "Transaction Denied!",
      balance: amount,
    });
  }
});
transaction.then((response) => {
    console.log(response); // This will be executed, if the promise was resolved
}).catch((error) => {
    console.log(error); // This will be executed, if the promise was rejected
}).finally(() => {
    console.log("I will be executed anyway"); // I will be executed anyway
})
```
When we run the above codes, we can get the following result. 
```
Trying transaction, for downpayment of amount $8000
▶︎{message: 'Successfully Approved!', balance: 2000}
I will be executed anyway
▶︎Promise {<fulfilled>: undefined}
```
When we change the amount to 5000, we can get the following result. 
```
Trying transaction, for downpayment of amount $8000
▶︎{message: 'Transaction Denied!', balance: 5000}
I will be executed anyway
▶︎Promise {<fulfilled>: undefined} 
```
___
## Promise Chaining
Promise chaining is a technique used that involves chaining multiple asynchronous operations in a specific sequence using promises. It is used to simplify and organize asynchronous code that involves multiple steps, by connecting the output of one promise to the input of another.
```javascript
let number = 20;

let getNumber = new Promise((resolve, reject) => {
  resolve(number);
  reject("Failed to get number");
});
getNumber
  .then((response) => {
    console.log(response, "MY 1st Time of Resolving!");
    return response * 5; // 100
  })
  .then((newResponse) => {
    // 100
    console.log(newResponse, "MY 2nd Time of Resolving!");
    return newResponse * 10; // 1000
  })
  .then((finalResponse) => {
    // 1000
    console.log(finalResponse, "MY final Time of Resolving!");
  })
  .then((anotherFinalResponse) => {
    console.log(anotherFinalResponse, "UHH LAST !"); // So here it will be undefined! Since I didn't return anything!
  });
```
___
## Using the fetch() API
fetch() is a web API used in JavaScript to make asynchronous HTTP requests to a server and handle the response. It returns a Promise object that resolves to the Response object representing the response to the request made. The fetch() function can be used to send GET, POST, PUT, DELETE, and other types of HTTP requests.

`.then()` and `.catch()`
```javascript
const fetchApi = fetch(
  "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json"
);
fetchApi
  .then((response) => {
    if (!response.ok) { //we are checking whether the network response is successful or not.
      console.log("Network response was not ok");
    }
    return response.json();
  })
  .then((finalOutput) => {
    console.log(finalOutput[0].name);
  })
  .catch((error) => {
    console.error("There was a problem with the fetch operation:", error);
  });

```
In this code, it is unnecessary to use `new Promise()` because the `fetch()` API itself returns a Promise that resolves to the Response object representing the network response. We then use the `.then()` method of the Promise returned by `fetch()` to specify a callback function that will be called when the network response is received. The first `.then()` block checks whether the network response was successful by checking the response.ok property. If the response is not ok, it logs an error message. If the response is ok, it returns the response data in JSON format by calling response.json().
The second `.then()` block receives the JSON data returned from the previous `.then()` block as an argument, and logs the name of the first product in the data.
The `.catch()` block is used to catch any errors that may occur during the fetch operation, and it logs an error message along with the error object.

`async/await`
```javascript
async function fetchApiAsyncAwait() {
  try {
    const response = await fetch("https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json");
    if (!response.ok) {
      console.log("Network response was not ok");
    }
    const finalOutput = await response.json();
    console.log(finalOutput[0].name);
  } catch (error) {
    console.error("There was a problem with the fetch operation:", error);
  }
}

fetchApiAsyncAwait();
```
In this code, we define an async function fetchApiAsyncAwait that uses await to wait for the fetch request to complete and for the response to be converted to JSON. We also use a try/catch block to handle any errors that might occur during the fetch operation.
___ 
## Promise.all
Promise.all is a method in JavaScript that takes an iterable of promises as input and returns a single Promise that resolves to an array of the results of the input promises. The returned Promise is fulfilled when all the input promises have been fulfilled or rejected if any of the input promises have been rejected.
If any of the promises is failing/rejected then the other resolved promises are ignored and it goes to the catch block.

`.then()` and `.catch()`
```javascript
async function fetchApis() {
  try {
    const [api1Response, api2Response] = await Promise.all([
      fetch('https://api.example.com/api1'),
      fetch('https://api.example.com/api2')
    ]);

    if (!api1Response.ok) {
      throw new Error(`API 1 returned an error: ${api1Response.status}`);
    }
    if (!api2Response.ok) {
      throw new Error(`API 2 returned an error: ${api2Response.status}`);
    }

    const api1Data = await api1Response.json();
    const api2Data = await api2Response.json();

    console.log('API 1 data:', api1Data);
    console.log('API 2 data:', api2Data);
  } catch (error) {
    console.error('There was a problem with the fetch operation:', error);
  }
}

fetchApis();
```
`async/await`
```javascript
function fetchApis() {
  Promise.all([
    fetch('https://api.example.com/api1'),
    fetch('https://api.example.com/api2')
  ])
  .then((responses) => {
    const [api1Response, api2Response] = responses;

    if (!api1Response.ok) {
      throw new Error(`API 1 returned an error: ${api1Response.status}`);
    }
    if (!api2Response.ok) {
      throw new Error(`API 2 returned an error: ${api2Response.status}`);
    }

    return Promise.all([api1Response.json(), api2Response.json()]);
  })
  .then((data) => {
    const [api1Data, api2Data] = data;

    console.log('API 1 data:', api1Data);
    console.log('API 2 data:', api2Data);
  })
  .catch((error) => {
    console.error('There was a problem with the fetch operation:', error);
  });
}

fetchApis();
```