# Understanding Promises in JavaScript

***This article was originally published at:*** [***https://blog.bitsrc.io/understanding-promises-in-javascript-c5248de9ff8f***](https://blog.bitsrc.io/understanding-promises-in-javascript-c5248de9ff8f)

JavaScript is a single-threaded programming language, which means only one thing can happen at a time. Before ES6, we used callbacks to handle asynchronous tasks such as network requests.

Using promises, we can avoid the infamous ‘callback hell’ and make our code cleaner, easier to read, and easier to understand.

Suppose we want to get some data from a server asynchronously, using callbacks we would do something like this:

```javascript
getData(function(x){  
    console.log(x);  
    getMoreData(x, function(y){  
        console.log(y);   
        getSomeMoreData(y, function(z){   
            console.log(z);  
        });  
    });  
});
```

Here I am requesting some data from the server by calling the `getData()` function, which receives the data inside the callback function. Inside the callback function, I am requesting some more data by calling the `getMoreData()` function passing the previously received data as an argument and so on.

This is what we call callback hell, where each callback is nested inside another callback, and each inner callback is dependent on its parent.

We can rewrite the above snippet using promises. For example:

```javascript
getData()  
  .then((x) => {  
    console.log(x);  
    return getMoreData(x);  
  })  
  .then((y) => {  
    console.log(y);  
    return getSomeMoreData(y);  
  })  
  .then((z) => {  
    console.log(z);  
   });
```

We can see that this is much cleaner and easier to understand than the callbacks example above it.

### What is a Promise?

A Promise is an object that holds the future value of an async operation. For example, if we are requesting some data from a server, the promise promises us to get that data that we can use in the future.

Before we get into all the technical stuff, let’s understand the terminology of a Promise.

#### States of a Promise

A Promise in JavaScript just like a promise in the real world has 3 states. It can be 1) unresolved (pending), 2) resolved (fulfilled), or 3) rejected. For example:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1665198509534/8dTL4afRy.png align="center")

States of Promise

* **Unresolved or Pending** — A Promise is pending if the result is not ready. That is, it’s waiting for something to finish (for example, an async operation).
    
* **Resolved or Fulfilled** — A Promise is resolved if the result is available. That is, something finished (for example, an async operation) and all went well.
    
* **Rejected** — A Promise is rejected if an error happened.
    

Now that we know what a Promise is, and Promise terminology, let’s get back to the practical side of the Promises.

### Creating a Promise

Most of the time you will be consuming promises rather than creating them, but it’s still important to know how to create them.

#### Syntax:

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

We create a new promise using the Promise constructor it takes a single argument, a callback, also known as the executor function which takes two callbacks, resolve and reject.

The executor function is immediately executed when a promise is created. The promise is resolved by calling the `resolve()` and rejected by calling `reject()`. For example:

```javascript
const promise = new Promise((resolve, reject) => {
    if(allWentWell) {
        resolve('All things went well!');
    } else {
        reject('Something went wrong');
    }
});
```

The `resolve()` and `reject()` takes one argument which can be a string, number, boolean, array or object.

Let’s look at another example to fully understand the promise creation.

```javascript
const promise = new Promise((resolve, reject) => {
    const randomNumber = Math.random();
    setTimeout(() => {
        if(randomNumber < .6) {
            resolve('All things went well!');
        } else {
            reject('Something went wrong');
        }
    }, 2000);
});
```

Here I am creating a new promise using the Promise constructor. The promise is resolved or rejected after 2 seconds of its creation. The promise is resolved if the `randomNumber` is less than `.6` and rejected otherwise.

When the promise is created, it will be in the pending state, and its value will be `undefined`. For example:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1665198510947/Skimi5Ttr.png align="left")

*Promise status pending*

After the 2 seconds timer finishes, the promise is either resolved or rejected randomly, and its value will be the value passed to the resolve or reject function. For example:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1665198512128/-1K6eywm5.png align="left")

*Promise status resolved*

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1665198513283/mynB7wTHq.png align="left")

*Promise status rejected*

**Note** — A promise can be resolved or rejected only once. Further invocation of `resolve()` or `reject()` has no effect on the Promise state. For example:

```javascript
const promise = new Promise((resolve, reject) => {
    resolve('Promise resolved'); // Promise is resolved
    reject('Promise rejected'); // Promise can't be rejected
});
```

Since `resolve()` is called first so the Promise will be resolved. Calling `reject()` after that will have no effect on the Promise state.

### Consuming a Promise

Now that we know how to create a promise, let’s understand how to consume an already-created promise. We consume a promise by calling `then()` and `catch()` methods on the promise.

For example, requesting data from an API using fetch which returns a promise.

`**.then()**` **syntax**: `promise.then(successCallback, failureCallback)`

The `successCallback` is called when a promise is resolved. It takes one argument which is the value passed to `resolve()`.

The `failureCallback` is called when a promise is rejected. It takes one argument which is the value passed to `reject()`.

For example:

```javascript
const promise = new Promise((resolve, reject) => {
    const randomNumber = Math.random();
    if(randomNumber < .7) {
        resolve('All things went well!');
    } else {
        reject(new Error('Something went wrong'));
    }
});

promise.then(
    (data) => {
        console.log(data); // prints 'All things went well!'
    },
    (error) => {
        console.log(error); // prints Error object
    }
);
```

Here if the promise is resolved, the `successCallback` is called with the value passed to `resolve()`. And if the promise is rejected, the `failureCallback` is called with the value passed to `reject()`.

`**.catch()**` **syntax**: `promise.catch(failureCallback)`

We use `catch()` for handling errors. It’s more readable than handling errors inside the `failureCallback` callback of the `then()` callback. For example:

```javascript
const promise = new Promise((resolve, reject) => {
    reject(new Error('Something went wrong'));
});

promise
.then((data) => {
    console.log(data);
})
.catch((error) => {
    console.log(error); // prints Error object
});
```

### Promise Chaining

The `then()` and `catch()` methods can also return a new promise which can be handled by chaining another `then()` at the end of the previous `then()` method.

We use promise chaining when we want to resolve promises in a sequence.

For example:

```javascript
const promise1 = new Promise((resolve, reject) => {
    resolve('Promise1 resolved');
});

const promise2 = new Promise((resolve, reject) => {
    resolve('Promise2 resolved');
});

const promise3 = new Promise((resolve, reject) => {
    reject('Promise3 rejected');
});

promise1
.then((data) => {
    console.log(data); // Promise1 resolved
    return promise2;
})
.then((data) => {
    console.log(data); // Promise2 resolved
    return promise3;
})
.then((data) => {
    console.log(data);
})
.catch((error) => {
    console.log(error); // Promise3 rejected
});
```

#### So what’s happening here?

* When `promise1` is resolved, the `then()` method is called which returns `promise2`.
    
* The next `then()` is called when `promise2` is resolved which returns `promise3`.
    
* Since `promise3` is rejected, the next `then()` is not called instead `catch()` is called which handles the `promise3` rejection.
    

**Note** — Generally only one `catch()` is enough for handling the rejection of any promise in the promise chain, if it’s at the end of the chain.

#### Common Mistake

A lot of beginners make the mistake of nesting promises inside another promise. For example:

```javascript
const promise1 = new Promise((resolve, reject) => {
    resolve('Promise1 resolved');
});

const promise2 = new Promise((resolve, reject) => {
    resolve('Promise2 resolved');
});

const promise3 = new Promise((resolve, reject) => {
    reject('Promise3 rejected');
});

promise1.then((data) => {
    console.log(data); // Promise1 resolved

    promise2.then((data) => {
        console.log(data); // Promise2 resolved

        promise3.then((data) => {
            console.log(data);
        }).catch((error) => {
            console.log(error); // Promise3 rejected
        });

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

}).catch((error) => {
    console.log(error);
});
```

Although this works fine, this is considered to be a poor style and makes our code difficult to read. If you have a sequence of promises to resolve, it’s better to chain promises one after another rather than nest one inside another.

### Promise.all( )

This method takes an array of promises as input and returns a new promise that fulfills when all of the promises inside the input array have been fulfilled or rejected as soon as one of the promises in the array rejects. For example:

```javascript
const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('Promise1 resolved');
    }, 2000);
});

const promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('Promise2 resolved');
    }, 1500);
});

Promise.all([promise1, promise2])
    .then((data) => console.log(data[0], data[1]))
    .catch((error) => console.log(error));
```

* Here the data argument inside the `then()` method is an array that contains promise values in the same order as defined in the promise array passed to `Promise.all()` (if all promises fulfill).
    
* The promise is rejected with the reason of rejection from the first rejected promise in the input array. For example:
    

```javascript
const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('Promise1 resolved');
    }, 2000);
});

const promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('Promise2 rejected');
    }, 1500);
});

Promise.all([promise1, promise2])
    .then((data) => console.log(data[0], data[1]))
    .catch((error) => console.log(error)); // Promise2 rejected
```

* Here we have two promises where one is resolved after 2 seconds, and the other is rejected after 1.5 seconds.
    
* As soon as the second promise is rejected after 1.5 seconds, the returned promise from `Promise.all()` is rejected without waiting for the first promise to be resolved.
    

This method can be useful when you have more than one promise, and you want to know when all of the promises have been resolved. For example, if you are requesting data from different APIs and you want to do something with the data only when all of the requests are successful.

So `Promise.all()` waits for all promises to succeed and fails if any of the promises in the array fails.

### Promise.race( )

This method takes an array of promises as input and returns a new promise that fulfills as soon as one of the promises in the input array fulfills or rejects as soon as one of the promises in the input array rejects. For example:

```javascript
const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('Promise1 resolved');
    }, 1000);
});

const promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('Promise2 rejected');
    }, 1500);
});

Promise.race([promise1, promise2])
    .then((data) => console.log(data)) // Promise1 resolved
    .catch((error) => console.log(error));
```

* Here we have two promises where one is resolved after 1 second, and the other is rejected after 1.5 seconds.
    
* As soon as the first promise is resolved after 1 second, the returned promise from `Promise.race()` is resolved without waiting for the second promise to be resolved or rejected.
    
* Here data passed to the `then()` method is the value of the first promise that resolves.
    

So `Promise.race()` waits for one of the promises in the array to succeed or fail and fulfills or rejects as soon as one of the promises in the array is resolved or rejected.

### Conclusion

We have learned what promises are and how to use them in JavaScript. A Promise has two parts 1) Promise creation and 2) consuming a Promise. Most of the time you will be consuming promises rather than creating them, but it’s still important to know how to create them.

That’s it and hope you found this article helpful! Please feel free to comment below and ask anything, suggest feedback or just chat. Cheers! ⭐️
