The complete guide to understanding Promise in JavaScript
Tags: javascript, webdev, beginners, programming
Published: 2022-10-10 21:51:43

Promise is one of the most essential concept to understand in JavaScript, yet it remains a challenging concept to grasp. Promise assist us to handle asynchronous operations in JavaScript. 

The concept of asynchronous in JavaScript enables us to break down complex projects into minor tasks.
We subsequently use any of these procedures : `callbacks`, `promise` or `async/await` to execute these minor tasks more effectively.

In this article, I intend to explain Promise to you in an easy-to-understand procedure. The article will explore the ensuing concepts 

- Asynchronous and Synchronous operations
- Callback and Callback hell
- Promise and the Promise lifecycle
- Promise Chaining
- Promise error handling

Before we dive into Promise, we need to first understand synchronous and asynchronous operations in JavaScript.

## Synchronous and Asynchronous Operations

JavaScript is a single-thread language, because it is single-threaded, it only authorizes one logic to be performed at a time. Code execution is in sequence, and must complete executing a statement before moving on to the next statement. 

The procedure defined above makes the operations in **JavaScript synchronous**. 

### Understanding Synchronous Operations

> In a synchronous system, tasks are completed one after the other.

Consider a scenario where you are tasked to lift **5 buckets filled with water at varying levels** from one destination to the other, and empty it into a bigger reservoir. 

![Synchronous JS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qzcp3x09vjlpcqs3rsew.jpg)

As long as you have only one hand, you complete the lifting of one bucket to its destination, pour the water into the required reservoir, before moving on to lift and empty the next bucket into the reservoir.


**Tasks are being completed one after the other making it synchronous**

There is always a challenge in performing these tasks one after the other (lifting, moving and emptying the buckets into the reservoir). 

For example, if there is difficulty in lifting the third bucket due to the high volume of water in it, it will prevent the completion of the other tasks. The other buckets (buckets 4 to 10) will have to wait for the current bucket to be moved to its destination and emptied into the reservoir before their session begins.

**In effect, the third bucket is delaying its task hence blocking all the remaining tasks from being completed**.

From the scenario above, if tasks are being **completed one after the other** in JavaScript, the procedure is synchronous, and synchronous operations in JavaScript is **blocking**. 

For example, **if a statement takes a while to execute, it will freeze the other statements from completing**.

Let's take a look at Asynchronous operations.

### Understanding Asynchronous Operations

JavaScript is a single-threaded language, however, JavaScript can behave as if it were multi-threaded and non-blocking. This means that, it doesn't wait for the response of an API call, I/O events, etc., and can proceed the code execution.

> Asynchronous programming is a technique that enables your program to start a potentially long-running task, and still be able to be responsive to other events while that task runs, rather than having to wait until that task has finished. Once that task has finished, your application is presented with the result.

Let's explore this technique further.

Take the same scenario where you have 5 buckets filled with water at different levels. But in this scenario, you have 5 helping hands (multiple hands) to lift the buckets from a point to their destination, and empty into the reservoir. 

Because you have 5 helping hands, **each hand can perform its task independently**. 

If one hand struggles to lift it assigned bucket to its destination due to the volume of water it contains, it can take all the time it needs to lift and empty the bucket into the reservoir, and this will not stop the other hands from completing their tasks.

With the scenario above, each task is complete independently, making it asynchronous.

Asynchronous operations in JavaScript  is **non-blocking**. Meaning, if a statement takes a while to execute, **it will not block the other statements from completing**.

### The challenge with long-running synchronous function.
In the above, we explored both synchronous and asynchronous operations in JavaScript.

To recap:
- In synchronous operations: tasks are **performed one after the other, and is blocking
**
- In asynchronous operations: **tasks are performed independently of other tasks, and is non-blocking**

Let's explore some challenges we might encounter with synchronous operations.

What if a synchronous operation takes a long time to complete, and there is a task which depends on an output from our earlier task (which is delaying).

What will happen to the rest of our tasks?

Well, we have no option, but to wait for the long running task to complete, before we can move on.
**This will freeze or block all the other tasks that might depend on the output of our long running task**

To figure out the issue of freezing our application, we need a way for our application to:
- Commence a long-running or delaying operation by calling a function. This function will execute when the delaying operation has completed.
- Inform us with the result of the long-running operation when it eventually completes.
- Run the other tasks independently.

That's exactly what asynchronous functions try to solve. If a function takes some time to complete its task, the rest of the code below that function can still run whiles the function is in progress, making our application non-blocking

### Example of asynchronous operation

```
// Declare some functions
function foo() {
	console.log("foo")
}
function bar() {
	console.log("bar")
}

//Executes the functions below
setTimeout(foo,2000) //Delays for 2s then executes foo

console.log("Faz")
bar()
``` 
By default, the execution of the code above should be **synchronous and blocking**, meaning tasks should executed one after the other.

See the illustration below:


![Async](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ej9ag9fmgd674byu7zl.jpg)

Because `setTimeout()` function  will wait for 2s before running the `foo`, it **should block or freeze** the other codes below it from executing.

However, the output of the code is:

```
"Faz"
"bar"
"foo"
``` 

Due to the JavaScript event-loop behavior, the entire code executes **asynchronously and non-blocking**.

This occurs because the event loop **first executes all synchronous code** and at the same time, running the `setTimeout()` in the background.

In the background, it waits for  2 seconds for the `setTimeout()`to complete, and when it's done, puts the callback that we passed as an argument in something called the task queue where it will be added to the call stack and executed.

Hence, the program above is executed independently and is non-blocking


## Understanding callback functions?

> One approach to asynchronous programming, is to make functions that perform a slow action take an extra argument, a callback function. The action is started in the background, and when it finishes, the callback function is called.

In the code above, you will notice, we passed `foo` as an argument to the `setTimeout()` function.

> A callback is a function that's passed into another function, with the expectation that, the passed function will be called at the appropriate time.

The code below illustrates `callback`

```
function task1(call_task2){
  
  console.log("Start task1, when done, start task2 ")
 //call task 2
  call_task2()
}

function task2(){
  console.log("Task2 has started...")
}
//pass task2 to task1
task1(task2)
``` 
The output of the code above will be 

```
"Start task1, when done, start task2 "
"Task2 has started..."
``` 

### Importance of callback functions ?
We have learnt what a callback function is, but why do we need `callback` functions?

> When performing a complex task, we break that task down into minor steps. To create a connection amongst these steps based on their order and time taken (optional), we use callback functions.

#### Use case of callback function

To better understand `callback functions`, let's imagine we run a fried rice street food business, where customers can order our delicious fried rice.


![Callback function](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1mxbruo4dixsn46x38h0.jpg)

Below we have outlined the steps to prepare our fried rice when the customer places an order

#### Steps in preparing fried rice
- Customer orders fried rice 
- Preparation begins
 -  Chop and prepare the ingredients ----- 3 seconds
 -  Season the meat and prepare the egg and rice ------ 2 seconds
 - Mix the ingredients ------ 2 seconds
 - Add soy sauce, oyster sauce, and salt to taste ------ 1 second
- Serve customer ----- 2 seconds

For our streetfood business to be profitable:

- The customer first orders the fried rice, and then we begin the production.

Let's establish a relationship between the ordering and the preparing of the fried rice.

See the code below:

```
function customerOrders(call_back){
 console.log("Customer places order")
//call startProducing
 call_back()
}

function startProducing(){
console.log("Begin fried rice preparation")
}

//pass startProducing function to customerOrders
customerOrders(startProducing)
``` 
In the code above:
- We defined a `customerOrders()` function to indicate the customer has placed an order
-  We defined a `startProducing()` function to start preparing the fried rice
-  To establish a relationship between the ordering and preparation of the fried rice, we will use a `callback` function. **Once, the order has been placed, then we will start the preparation**
- To achieve the above, we will pass the `startProducing` function as an argument to the `customerOrders` function.

The output of the code above will be :

```
"Customer places order"
"Begin fried rice preparation"
``` 
Let's assume the customer is undecided, so he waits for 2 seconds before placing the order.After 2 seconds, the order will be placed, and we can start preparing our delicious fried rice. 

Let's expand our code to accommodate the 2 seconds delay using the `setTimout()` function.


```
function customerOrders(call_back){
//wait for 2 seconds and runs the inner function
setTimeout(function(){
 console.log("Customer places order")
//call startProducing function
 call_back() 
},2000)

}

function startProducing(){
console.log("Begin fried rice preparation")
}

customerOrders(startProducing)
``` 

- We have still kept the relationship between ordering and preparation of the fried rice by passing the `startProducing` as the callback function to `customerOrders`.

We also introduce another aspect, the time it takes before the customer places an order.
- The `setTimeout()` is another function we are nesting into the `customerOrders()` function. 
- When the `customerOrders()` function is executed, it will run the `setTimeout` function in the background
- After two seconds delay, the function passed to the `setTimeout` will execute. 

Note: In the  body of that function, we finally run our `startProducting` function.
Because we are passing functions to other functions, and running functions in functions, we are effectively creating callback functions. The key takeaway is, there is a link between all these functions.

So far, we have used callback functions to perform to important steps:
- Allow 2 seconds delay, and then customer places an order
- Start preparing the fried rice, when customer places an order.

Now that the customer has placed the order, let's start preparing the fried rice.

We will insert all the specific tasks to be carried out  in preparing the fried rice in our `startProducing` function. This is to form a connnection between each task.

Because each individual task takes some time to complete, we will use the `setTimeout` function to stimulate the  time it takes to perform that task in the background.

After the task has been completed, we will inform the customer about the action that has been taken.

The snippet for the `setTimeout` function is as below:

```
setTimeout(function(){
 	//code goes here
}, 2000); //Time before execution
``` 
Let's expand our code to include the specific tasks to be performed.

- For our first task, it will take 3 seconds to complete the process. After the duration has elapse, we will execute the code passed to the `setTimeout`

```
//customer places order
function customerOrders(call_back) {
  //wait for 2 seconds
  setTimeout(function () {
    console.log("Customer places order");
    //start preparing the fried rice.
    call_back();
  }, 2000);
}

//start fried rice preparation
function startProducing() {
  setTimeout(function () {
    console.log("Begin fried rice preparation");

    //delay for 3s and run the function
    setTimeout(function () {
      console.log("Chopped and prepared the ingredients");
    }, 3000);
  }, 0000);
}
//run the tasks
customerOrders(startProducing);

``` 
The output will be:

```

"Customer places order"
"Begin fried rice preparation"
"Chopped and prepared the ingredients"

``` 

Let's complete the preparation of our fried rice in the `startProducing()` function by merging each next step to take inside the previous step.This will help establish a link between each step.

The illustration for the code execution is as below:


![Promise Illustration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qphifbvmkrud4tql9ri8.jpg)

The entire code is as below:

```
function customerOrders(call_back) {
  //customer wait for 2 seconds
  setTimeout(function () {
    console.log("Customer places order");
    //call startProducing function
    call_back();
  }, 2000);
}
// Start preparing the fried rice
function startProducing() {
//step 1
  setTimeout(function () {
    console.log("Begin fried rice preparation");
    //step 2
    setTimeout(function () {
      console.log("Chopped and prepared ingredients");
      // step 3
      setTimeout(function () {
        console.log("Seasoned the meat and have prepared the egg and rice");
        //step 4
        setTimeout(function () {
          console.log("Ingredients have been mixed");
          //step 5
          setTimeout(function () {
            console.log("Added soy sauce, oyster sauce, and salt to taste");
            //step 6
            setTimeout(function () {
              console.log("Serving the delicious fried rice");
            }, 2000);
          }, 1000);
        }, 4000);
      }, 2000);
    }, 3000);
  }, 0000);
}

customerOrders(startProducing);

``` 
The output of the code will be :

```
"Customer places order"
"Begin fried rice preparation"
"Chopped and prepared ingredients"
"Seasoned the meat and have prepared the egg and rice"
"Ingredients have been mixed"
"Added soy sauce, oyster sauce, and salt to taste"
"Serving the delicious fried rice"
``` 

We have learnt how to perform a sequence of tasks using callback functions. 

We can use the same callback function technique,when fetching multiple data from an api. Where we rely on the result of the initial api call to perform the next action.

But this approach introduces another layer of complexity.

Let's explore further.

### Welcome to callback hell 
Eventhough we have been able to prepare our delicious fried rice, the code above looks messy. We needed to nest **callback **into **callback **into **callback**. This phenomenon is termed `callback hell`.

> Callback hell is the situation where callbacks are nested within other callbacks several levels deep, potentially making it difficult to understand and maintain the code.

![Callback hell](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c6d48ro8gqcp5e3qo22o.png)

Can you spot the **pyramid shape highlight** in the yellow section below, and all the `})` at the end of our program? This is graciously known as **callback hell**.



**If we nest callbacks in such a way describe above, we are more likely to write, difficult to read, error prone and difficult to maintain code.** The solution is to use `Promise` to handle each tasks. 

In the next section, we dive into `Promise`

## Introducing Promise
In an asychronous operation (tasks that takes sometime to complete), instead of arranging for a function to be **called at a later time** (callback function), you can **return an object that represents a future value**. 

The return object is what we call `Promise`.This object represents the future completion (or failure) of an asynchronous operation and its resulting value.

In simple terms, a `Promise` is **container for a future value**. An example of a future value is a response from an AJAX call or fetching data from an API using the `fetch()` method.


> A promise is an asynchronous action that may complete at some point and produce a value. It is able to notify anyone who is interested when its value is accessible. We can also define Promise, as an object that is used as a placeholder for the future result of an asynchronous operation.

### Promise analogy 
We can appreciate the concept of Promise using the analogy below:

- When you place a bet on a football match, there is a **promise ** that you will receive money if you guess the correct outcome of the game. 
- So you purchase  the betting slip (promise) right away.
- On match day, if you guess the correct outcome, you will **receive the money,because it was promised.**

Using our initial fried rice scenario,if a customer places an order for our delicious fried rice, we will give the customer something to hold on to (for instance a slip).
The slip is a  **promise** to the customer we would get him our delicious fried rice, if he orders now.
With this assurance, he can wait for sometime as we prepare the fried rice. 

We **promise to  attend to** the customer in any case:
- If we were able to prepare the fried rice, the customer is informed, and he can enjoy his fried rice
- If something happened (perhaps, we did not have enough rice), and we couldn't prepare the fried rice, we will still notify the customer, and the customer goes home disappointed.

### Advantages of Promise

- Using Promise, we no longer need to rely on events and callbacks passed into asynchronous functions to handle asynchronous results.
- Instead of nesting callbacks, we can chain promises for a sequence of asynchronous operations; escaping callback hell.


### Creating a  Promise
The syntax for creating a `Promise` is as below:

```
let success = true;

let promise = new Promise(function(resolve, reject) {
  // perform an asynchronous operation here...
 
//return state of the promise
  if (success) {
    resolve(value);
  } else {
    reject(error);
  }
});
``` 
Let's understand the code above:

- We call the `new Promise()` constructor and pass it a callback function as an argument
- The callback passed to the `new Promise()` is the executor, and is responsible for performing an asynchronous operation.
- The callback accept two parameters, `resolve` and `reject` 
- Inside the body of the callback, we perform any asynchronous operation such as fetching data from an API.
- If the asynchronous operation **successfully completes**, and we get some kind of value, we will call the  `resolve()` function and pass it the value. 
- If the asynchronous operation was **unsuccessfull** perhaps due some slow internet connectivity, we will call the `reject()` function, and pass it the error.

The returned value of `new Promise()` is an `Object`, this `Object` serves as a placeholder for value we will get back from the asynchronous operation.

### Understanding the Promise Lifecycle

- Whiles the asynchronous task in being carried, the `Promise Object` returned, will have a `State` property with an inital value of `Pending`
- When the asynchronous task completes, the value of the `State` property updates to `Settled`
- If the asynchronous task was **successful**, the `State` property updates to `fulfilled`
- If the asynchronous task was **unsuccessful**, the `State` property updates  to `rejected`
- There is also a `result` property in the Promise object. It is initially  set to `undefined`, then changes to the value we got from the asynchronous operation when `resolve()` is called or `error` when `reject(error)` is called.


![Promise JavaScript](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sn9hsz8955hz4rlfo3hl.jpg)

The examine that  `new Promise()` constructor function returns a `Promise Object`

```
const promise = new Promise((resolve,reject)=>{
  
})

console.log(promise)
``` 
The output of the code above will be 


![Promise JS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l137k373jxkckaafys7u.png)

> To sum up,the callback function we pass to the `new Promise()` constructor runs automatically and attempts to perform a task.** When it is done with the attempt, it calls `resolve()` if it was successful or `reject()` if there was an error.**



![Callback](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5xksuoh8cccslecdg608.jpg)

Examine the code below:

```
const getSomething = ()=> {
    return new Promise(function(resolve,reject){
// perform some async operation here...

let success = true  // Stimulate successful async operation
  
  if (success) { 
    resolve("data"); // pass the value you got from the async operation
  } else {
    reject(error); // pass the error if unsuccessful
  }
    })
}

console.log(getSomething())
``` 
- We declared the `getSomething` function, in the body of the function, we call the `new Promise` constructor function
- We passed the callback function, and it the body of the callback, we perform the asynchronous operation. 
- If the operation was successful, we call the `resolve()` and pass it the data we got from the async operation.
- If the operation was unsuccessful, we call the `reject()` and pass it the error we got from the async operation.

Below will be the result of the code above:


![Promise fulfilled](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/34mss7iu0rff8nrgywvq.png)

### Promise and the fetch API.

Inside of using the AJAX call to make a request for resources on a web server, the global `fetch()` method provides an easy and logical way to fetch resouces asynchronously across the network.

When the `fetch()` method is executed, it immediately returns a `Promise Object` , and goes through the Promise lifecyle as described above.

A basic fetch request is easy to set up:

```
const promise = fetch('http://www.example.com')
console.log(promise)
``` 
The output will be


![Promise Fetch](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ko8i66lg01d6u4cez0t.png)


### Consuming Promises using the `then()` method
We need to find a way of utilizing the `Promise Object` returned after the  completion of the asynchronous task.The `then()` method will help with that

> To get the value of a promise when it’s fulfilled, you call the `then()` method of the promise object. 

The following shows the syntax of the `then()` method:

```
// consuming the promise object
promise.then(
  function(result) { /* handle a successful result */ },
  function(error) { /* handle an error */ }
);

``` 
- In the `then()` method, we accept two callback functions.
- If the `Promise State` is **fulfilled**, we run the first function. In the function we accept one parameter,  the `result` of the asynchronous task. We can now make use of the `result`.
- If the `Promise State` is **rejected**, we run the second function,it also accept the `error` we got from the asynchronous task as a parameter.

Examine the code below:

```
const getSomething = ()=>{
  return new Promise((resolve,reject)=> {
// perform some async operation here...
 let success = true //async operation was successful
  if (success) {
    resolve("We got some data back");
  } else {
    reject("Oops Error fetching data");
  }
    })
  
  })
}

//get the value of the promise object using then()
getSomething().then(
function onFulfilled(result){
/* handle a successful result */
  console.log(result)
}, function onRejected(error){
/* handle an error*/
  console.log(error)
})
``` 
Let's understand the code above:

-  We utilize the `then()` method to consume or make use of what we have in the `Promise object`.Whether the asynchronous operation was successful or not, we want to know and take some action.
- The first argument of the `.then()` is a function that runs when the promise is **fulfilled**.
- The second argument of the `.then()` is a function that runs when the promise is **rejected **
- If we are interested in only the successful completion, we can insert only one callback function in the `.then()` method.
- In the code above, we pass to the `.then()` method both the `onFulfilled` function and `onRejected` function.
- In the body of the function, we can log the result or the error if any.

The output of the code above will be:

![Fetch API](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6q8ehquboo8vkyn9cost.png)

Another example using the `fetch()` method

```
const result = fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then(response => response.json(), error => console.log(error) )

console.log(result)
``` 
The output will be:


![JavaScript Promize](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ovl8ol0kj7u4393xdh2.png)

### Using the `.catch()` method to handle errors
If we are interested only in the error when the Promise is `rejected`, we can use the `catch()` method of the Promise object to "catch" the error

```
promise.catch((error) => {
  console.log(error);
});
``` 
We can rewrite the code above as :

```
const getSomething = ()=> {
  return new Promise((resolve,reject)=> {
// perform some async operation here...
 let success = false //async operation was unsuccessful
  if (success) {
    resolve("We got some data back");
  } else {
    reject("Opps Error fetching data");
  }
    })
  

}

//get the value of the promise object using then()
getSomething().then(
function onFulfilled(result){
/* handle a successful result */
  console.log(result)
}).catch((error)=>{
/* handle a unsuccessful result */
  console.log(error)
})
``` 
Using the `fetch()` method

```
const result = fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then(response => response.json()).catch(error => console.log(error))

console.log(result)
``` 

## Promise Chaining 
Often, we need to execute two or more related asynchronous tasks. When the previous task succeeds, we want to start the next task with the result of the previous task. Promise chaining help us to accomplish that.

Examine the code below:
```
const doSomething = ()=>{
  return new Promise((resolve,reject)=>{
    //stimulate asynchronous operation
    setTimeout(function(){
      resolve(10)
    },3000)
  })
}
//consume the returned promise object
const secondPromise = doSomething().then((result)=>{
  console.log(`Result is ${result}`)
  //dosomething else with the result and return the update Promise object
  return result*2
})

console.log('secondPromise Object',secondPromise)
``` 
- In the code above, we used the `setTimeout` function to stimulate an asynchronous task.
- We wait for 3 seconds, and if the asynchronous task was successful, we call the `resolve()` method, and pass it a value of `10`.
- We get back a `Promise Object` with `PromiseState` and `PromiseResult` properties.
- Since the operation was successful, we change the `PromiseState` property of the Promise object from `Pending` to `fulfilled`, and the `PromiseResult` to `10`.
- In the `then()`method we can make use of the Promise object, we implement a callback function have access to the `PromiseResult` and log it to the console.

What if we want to perform another task with the `PromiseResult`, how do we handle that ?

- We can update the value in the `PromiseResult`and then return the Promise object. 

```
//update the result and return the Promise object
return result * 2
``` 

To examine that, the `then()` method can ** return updated Promise object,** we  can assign the returned Promise object to the `secondPromise` variable.



![Promise 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3qh7s1e5n9gedfovnocz.png)

-  From the above screenshot, the Promise Object was updated, and now has a `PromiseResult` of `20`. 
- We can even update this result by **chaining another .then() method** or simply log the result to the console.

See the modified code below

```
const doSomething = ()=>{
  return new Promise((resolve,reject)=>{
    //stimulate asynchronous operation
    setTimeout(function(){
      resolve(10)
    },3000)
  })
}
//consume the promise object using promise chaining
doSomething().then((result)=>{
  console.log(`Result is ${result}`)
  //dosomething else with the result
  return result*2
}).then((nextResult)=> {
// then do something else again
  console.log(`The next operation resulted in ${nextResult}`)
})

``` 


The output will be:

```
"Result is 10"
"The next operation resulted in 20"

``` 

How did we arrive at the about output?

- The first .then() method returned a Promise object, this Promise object was utilized by the second `.then()` method.
- We can keep calling successive `then()` method whiles returning the Promise Object.

> Essentially `Promise chaining`, is linking a series of `then()` method to define what needs to be done when the previous promise is fulfilled.

Basically, each promise represents the completion of another asynchronous step in the chain.

Promise chaining works this way:
- If our first task is complete, **then ** we do the other task, **then **we do the next task, **then **the next, **then **the next etc.

The below illustrates Promise chaining using the `fetch()` method

```
fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then(response => response.json())
  .then(json => console.log(json))
``` 
- Our first `then()` returns a Promise Object with a fulfilled state, we can chain a new `then()` do something else with the result returned by the previous `then()`

In time past, performing several asynchronous operations in a row would lead to the classic callback pyramid of doom.

### Solving the callback hell using Promise chaining
Let's now see how to use Promise to solve our Callback hell encounted in our Fried rice scenario

If the shop is open, the customer waits for 2 seconds and makes his order.

Below is  how to illustrate this process using Promises:

```
const customerOrder = (time, task)=>{
      let isOpen = true;
  return new Promise((resolve,reject)=>{
    if(isOpen){
      setTimeout(()=>{
        //run first task if async operation is successful
        resolve(task())
      
      },time)
    }else {
      reject("shop is closed")
    }
  })
  
}
customerOrder(2000,()=>{console.log('Customer places order')})

``` 

- If the shop is open, the customer waits for 2 seconds and places the order
-  We pass two arguments to the `customerOrder` function
  - First is the duration it takes to complete the task
  - Second, the task to perform
- The actual task will be performed in the `resolve()` method, doing this, we get access to the fulfilled Promise object

Now, instead of nesting callback into callback, we are going to use `Promise` chaining to define what needs to be done when the customer places an order.

Think of it as giving instructions to your cooks on what needs to be done after the customer places an order.

- You tell the cook to "Do this task first, **then **do the next task, **then **this other task, **then**..., **then**..., **then**..." etc.

The entire process is captured below:

- When the `customerOrder()` function is executed, it returns our `Promise Object`.
- The `Promise Object` will indicate whether our promise is fulfilled or rejected.
- If fulfilled, we run our tasks.
- Each of our tasks will be in the `then()` method.
- For each task, we will return our updated Promise Object.
  

The complete code is as below:

```
const customerOrder = (time, task) => {
  let isOpen = true;
  return new Promise((resolve, reject) => {
    if (isOpen) {
      setTimeout(() => {
        //run each task here if shop is open
        resolve(task());
      }, time);
    } else {
      reject("shop is closed");
    }
  });
};

customerOrder(2000, () => {
  console.log("Customer places order");
})
  // then task 1
  .then(() => {
    return customerOrder(0000, () => {
      console.log("Begin fried rice preparation");
    });
  })
  // then task 2
  .then(() => {
    return customerOrder(3000, () => {
      console.log("chopped and prepared the ingredient");
    });
  })
  // then task 3
  .then(() => {
    return customerOrder(2000, () => {
      console.log("seasoned the meat and prepared egg stew");
    });
  })
  // then task 4
  .then(() => {
    return customerOrder(4000, () => {
      console.log("Ingredients have been mixed");
    });
  })
  // then task 5
  .then(() => {
    return customerOrder(1000, () => {
      console.log("Added soy sauce, oyster sauce, and salt to taste");
    });
  })
  // then task 6
  .then(() => {
    return customerOrder(2000, () => {
      console.log("Serving the delicious fried rice");
    });
  })
  .finally(() => {
    console.log("Thank you for stopping by ");
  });

``` 
The output will be:

```
"Customer places order"
"Begin fried rice preparation"
"chopped and prepared ingredient"
"seasoned the meat and prepared egg stew"
"Ingredients have been mixed"
"Added soy sauce, oyster sauce, and salt to taste"
"Serving the delicious fried rice"
"Thank you for stopping by "
``` 

### The .finally() method
Occasionally you want to execute similar function whether the promise is fulfilled or rejected.
Examine the code below:

```
const doSomething = ()=>{
  // let run some task here
}

const getSomething = ()=> {
  return new Promise((resolve,reject)=> {
// perform some async operation here...
 let success = false //async operation was unsuccessful
  if (success) {
    resolve("We got some data back");
  } else {
    reject("Opps Error fetching data");
  }
    })
  

}

//get the value of the promise object using then()
getSomething().then(
(result)=> {
  /* handle a successful result */
  console.log(result)
// call doSomething
  doSomething()
}
).catch((error)=>{
/*handle an unsuccessful task */
  console.log(error)
// call doSomething
  doSomething()
})
``` 
Notice that, `doSomething()` function is duplicated in the both the `then()` method and `catch()` method. 

> To take out this duplication and execute the `doSomething()` whether the promise is fulfilled or rejected, you use the `finally()` method

The entire code will be :

```

const doSomething = ()=>{
  // let run some task here
}
const getSomething = ()=> {
  return new Promise((resolve,reject)=> {
// perform some async operation here...
 let success = false //async operation was unsuccessful
  if (success) {
    resolve("We got some data back");
  } else {
    reject("Opps Error fetching data");
  }
    })
  

}

//get the value of the promise object using then()
getSomething().then(
(result)=> {
  /* handle a successful result */
  console.log(result)

}
).catch((error)=>{
/*handle an unsuccessful task */
  console.log(error)

}).finally(()=>{
//finally, run this function
  doSomething()
})
``` 
## Summary
-  We can define Promise, as an object that is used as a placeholder for the future result of an asynchronous operation
- A promise begins in the  `Pending` state and ends in either `fulfilled` or `rejected` state.
- Utilize the `then()` method to arrange a callback to be executed when the promise is fulfilled, and `catch()` method to handle a callback to be executed when the promise is rejected.
- Place the task to be execute in the finally() method whether the promise is fulfilled or rejected.

If you have found value in this article, kindly share on your social media platform.
Do you need some clarification or you have a suggestion? Feel free to post your comment, I would love to read from you.
Don't forget to follow me on [Twitter](https://twitter.com/emmanuelfkumah)