Understanding Promises in 100 Seconds
Tags: javascript, beginners, programming, webdev
Published: 2024-04-03 03:09:32

## What is Promises?
Promises are the foundation of asynchronous processing in modern JavaScript. A promise is an object returned by an asynchronous function, which represents the current state of the operation.


## Asynchronous processing
Asynchronous processing enables various workflow processes to run at the same time.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r1bskz5vaeugq1bfqxvl.png)


## There states of Promises

* Pending - Initial state, The operation is still going on.
* Fulfilled - Meaning that the operation was completed successfully.
* Rejected - Meaning that the operation failed.


## Anatomy of Promises

```JavaScript
new Promise(function(resolve, reject) {
    resolve('Success');
});

new Promise(function(resolve, reject) {
    reject('Failed');
});
```


The promise constructor takes one argument which is a callback with two parameters, **resolve(success)** and **reject(error)**. Do whatever code you need inside the callback function. And you have to resolve().
reject() is optional if you don’t have errors.


## Why do we use Promises?
Asynchronous operations were typically handled using callback functions, which can lead to deeply nested code structures, often referred to as **callback hell** or **pyramid of doom**. This makes the code harder to read, maintain, and debug. 
Here is an example.

```JavaScript
// Task 1
setTimeout(() => {
  console.log("Task 1 completed");

  // Nested Task 2
  setTimeout(() => {
    console.log("Task 2 completed");

    // Nested Task 3
    setTimeout(() => {
      console.log("Task 3 completed");

      // Nested Task 4
      setTimeout(() => {
        console.log("Task 4 completed");
        // This could go on...

      }, 1000); // End of Task 4

    }, 1000); // End of Task 3

  }, 1000); // End of Task 2

}, 1000); // End of Task 1
```

Promises provide a more linear and readable code structure by chaining .then() handlers.
Here is the improved codes.

```JavaScript
function simulateAsyncTask(taskName, duration) {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log(taskName + ' completed');
      resolve();
    }, duration);
  });
}

// Using the function to chain tasks
simulateAsyncTask('Task 1', 1000)
  .then(() => simulateAsyncTask('Task 2', 1000))
  .then(() => simulateAsyncTask('Task 3', 1000))
  .then(() => simulateAsyncTask('Task 4', 1000));
```


## How to use Promises
There's two ways to use Promises.

1. Using **then()** and **catch()**
2. Using **async, await**


## then(), catch()
Every codes inside the **.then()** callback happens when the **promise is resolved**. The second "then" will **NOT execute** until first one is done. This connection with the “then” word is called a **Promise chain**.
On the other hand, every codes inside the **.catch()** callback happens when the **promise is rejected**.
Here is an example.

```JavaScript
const myPromise = new Promise((resolve, reject) => {
  let condition = true;
  if (condition) {
    resolve('Promise resolved successfully.');
  } else {
    reject('Promise rejected.');
  }
});

myPromise
  .then((message) => {
    console.log(message);
  })
  .catch((error) => {
    console.error(error);
  });

```
"myPromise" is a promise that resolves if condition is true, and rejects otherwise. **.then()** is used to handle the successful resolution of the promise, logging the success message to the console. **.catch()** is used to handle the case where the promise is rejected, logging the error message to the console.


## async, await
Async/Await can also do the same thing like using "then". It's cleaner and readable. When you use **await**, you must put the **async** in front of the function. The second await will **NOT execute** until the first one is done.
Here is an example.


```JavaScript
const myPromise = new Promise((resolve, reject) => {
  let condition = true;
  if (condition) {
    resolve('Promise resolved successfully.');
  } else {
    reject('Promise rejected.');
  }
});

// Here is changed!
async function handleMyPromise() {
  try {
    const message = await myPromise;
    console.log(message);
  } catch (error) {
    console.error(error);
  }
}

handleMyPromise();

```
We create "myPromise" resolve if a condition is true and reject otherwise. The "'handleMyPromise" function is declared as **async**, which allows us to use **await** within it. The **await** pauses the function's execution until the promise is either resolved or rejected. If myPromise resolves, we print its resolved value. If it rejects, we catch and log the error.


## Use case
One of the use cases of Promises is fetching API.
Here is an example.

```JavaScript
function fetchDummyData() {
  const dummyUrl = 'https://example.com/dummy.json';

  fetch(dummyUrl)
    .then(response => {
      if (!response.ok) {
        throw new Error('Network response was not ok');
      }
      return response.json();
    })
    .then(data => {
      console.log('Fetched dummy data:', data);
    })
    .catch(error => {
      console.error('Fetching dummy data failed:', error);
    });
}

fetchDummyData();

```
We call "fetch()" with a dummy URL. "fetch()" returns a Promise that resolves to the response of the request.
The first **.then()** receives the response. If the response is resolved, we call ".json()" which returns another Promise that resolves with the result of parsing the response body text as JSON. If not, we throw an error.
The second **.then()** receives the parsed JSON data, and we log it to the console. If there's an error, at any point in the chain, the **.catch()** callback function catches it, and we log the error.


## Conclusion
Promises is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value.