An introduction to promises in JavaScript
Tags: javascript, asynchronous
Published: 2019-11-23 12:26:21

Promises allow us to perform asynchronous operations. A `Promise` is a proxy, a placeholder if you will, for a value that is not necessarily known when we declare the promise. Instead of immediately having the final value, we have a _promise_ that a final value will be there.

They are useful when we need to do things such as store or retrieve data from a database or get data from an API.

## How to create a Promise

To create a promise we simply need to make a new instance of the object and pass a function as a parameter with the `resolve` and `reject` parameters.

```javascript
const promise = new Promise((resolve, reject) => /* do things */)
```

`resolve` will be called if the asynchronous action completes successfully and `reject` will be called if it doesn't. A promise can have three different states:
- `pending` is its initial state, it means that it hasn't completed yet
- `fulfilled` means that the operation has resolved or completed successfully
- `rejected` means that the operation has failed

So when the promise is first created, its state will be `pending`. Then, once the asynchronous operation has taken place, if it resolved successfully its state will become `fulfilled` and it will call the function `resolve`. Otherwise, it will be `rejected` and call the function `reject`.

So a quick example of a promise could look like this:

```javascript
const promise = new Promise((resolve, reject) => {
   console.log('Asynchronous operation started')
   setTimeout(() => Math.random() > 0.15
      ? resolve('Success!')
      : reject('Oops, something went wrong!')
   , Math.random() * 700 + 800)
})
```

The first thing that we'll get here is a message in our console letting us know that the operation has started. Then, after 0.8 to 1.5 seconds, the promise will either resolve (~85% of the time) and return a success message or fail (~15% chance) and return a failure message.

## `then` and `catch` or what happens when the promise resolves

Most often, after the asynchronous operation has resolved, we'll want to do something with the returned data. If, for example, we are retrieving information from a database, we might want to actually use that information. That's where the methods `then` and `catch` come in handy.

### `then`

The method `then` accepts two optional parameters, `onFulfilled` and `onRejected`. The first one will be called if the promise is `fulfilled` and the second one if it is `rejected`. Both functions will get one argument, which is the value returned by the promise.

Building on our previous promise, it could look something like this:

```javascript
promise.then(data => {
   writeMsg(data) // Writes 'Success!'
   launchFireworks() // Launches fireworks
}, rejection => {
   writeMsg(rejection) // Writes 'Oops, something went wrong!'
   playDefeatMusic() // Plays sad, defeat music
})
```

Often, though, you'll just want to pass the `onFulfilled` parameter and leave the logic that deals with rejection for a `catch` method. So you we could just write this:

```javascript
promise.then(data => {
   writeMsg(data)
   launchFireworks()
})
```

If you only need to pass one function to the `then`, you can just pass its name and the `then` will take care of calling it and passing the result of the promise as the function's argument.

```javascript
//Both these thens do the same
promise.then(data => doStuff(data))
promise.then(doStuff)
```

### `catch`

The method `catch` accepts the parameter `onRejected`, which will be called if the promise rejects. Other than that, it works exactly as `then`.

```javascript
promise
   .then(data => {
      writeMsg(data)
      launchFireworks()
   })
   .catch(error => {
      writeMsg(error)
      playDefeatMusic()
   })
```

And just like `then`, you can use shorthand when calling it:

```javascript
promise
   .then(doStuff)
   .catch(logError)
```

### Chaining `then` and `catch`

Whatever is returned by `then` and `catch` will also be wrapped in a promise. So it is possible to chain them even if they are not really doing asynchronous stuff.

```javascript
promise
   .then(transformData)
   .then(doMoreAsyncStuff)
   .then(transformData)
   .catch(dealWithError)
```

But maybe it's time we looked at a real example, instead of something filled with mock functions. Let's suppose that we are using MongoDB to store data about our exercise sessions. At some point, we want to retrieve said data. So we could do something like this:

```javascript
const mongoDB = require('mongodb')

mongoDB.MongoClient.connect(URI)
   .then(client => client.db('exercise'))
   .then(db => db.collection('workouts').find(query))
   .then(data => data.toArray())
   .then(console.log)
   .catch(console.warn)
```

This creates a connection to our MongoClient, which already returns a promise by itself. Then it selects the database `exercise`. Then it selects the collection `workouts` and looks for something that matches the criteria specified in `query`. Then it transforms the returned data into an array. Then, if everything has gone well, it logs the data into our console. If anything goes awry in the process, it will log it as a warning in the console.

## Making a function that returns a promise

If we use MongoDB, `fetch` or any function that returns a promise, we can just chain `then` and `catch` methods to it and that's all that we need to do to work with promises. But this isn't always the case. Sometimes, we might need to create a function that returns a promise first.

For example, let's imagine that for our exercise database we decided to use some database whose API for JavaScript doesn't return promises. Instead, it takes callbacks to deal with the returned data. So we would have to do something like `DbHandler.find(query, callback)` when we want to do something with retrieved information. And let's imagine that the callback should take two parameters `data` and `error`, which will be the retrieved data and the errors that might have happened.

Then we can create a function that looks up stuff in the database and returns it as a promise:

```javascript
const findPromise = query => new Promise((resolve, reject) => {
   DbHandler.find(query, (data, error) => {
      if (error == null) return resolve(data)
      else return reject(error)
   }
})
```

And now when we want to look up stuff in our database, we can call our crafted function like any other function that returns a promise:

```javascript
findPromise(query)
   .then(doStuff)
   .catch(console.warn)
```