# How to use promises in JavaScript

> Learn how to use JavaScript promises, including pending, fulfilled and rejected states, chaining, error handling, and promise concurrency methods.

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2018-02-09 | Updated: 2026-07-18 | Topics: [JavaScript](https://flaviocopes.com/tags/js/) | Canonical: https://flaviocopes.com/javascript-promises/

<!-- TOC -->

- [Introduction to promises](#introduction-to-promises)
  - [How promises work, in brief](#how-promises-work-in-brief)
  - [Which JavaScript APIs use promises?](#which-javascript-apis-use-promises)
- [Creating a promise](#creating-a-promise)
- [Consuming a promise](#consuming-a-promise)
- [Chaining promises](#chaining-promises)
  - [Example of chaining promises](#example-of-chaining-promises)
- [Handling errors](#handling-errors)
  - [Cascading errors](#cascading-errors)
- [Orchestrating promises](#orchestrating-promises)
  - [`Promise.all()`](#promiseall)
  - [`Promise.allSettled()`](#promiseallsettled)
  - [`Promise.any()`](#promiseany)
  - [`Promise.race()`](#promiserace)
- [Common errors](#common-errors)
  - [Calling Promise incorrectly](#calling-promise-incorrectly)

<!-- /TOC -->

## Introduction to promises

A promise represents the eventual completion or failure of an asynchronous operation.

It starts in the `pending` state. It then becomes `fulfilled` with a value, or `rejected` with a reason.

Promises were standardized in [ES2015](https://flaviocopes.com/es6/). [Async functions](https://flaviocopes.com/javascript-async-await/) provide a cleaner syntax for consuming promises, but they don't replace them.

Understanding promises is fundamental because many JavaScript APIs return them. The [MDN Promise reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) lists every current method.

### How promises work, in brief

When you create a promise, it starts in the **pending** state.

The surrounding synchronous code keeps running. Later, the promise becomes **fulfilled** or **rejected**. A promise in either state is called **settled**.

You might also see the word *resolved*. A resolved promise can still be pending if it follows another promise, so it is not another name for fulfilled.

### Which JavaScript APIs use promises?

In addition to your own code and library code, promises are used by standard Web APIs such as the [Fetch API](https://flaviocopes.com/fetch-api/) and many [Service Worker](https://flaviocopes.com/service-workers/) methods.

It's unlikely that in modern [JavaScript](https://flaviocopes.com/javascript/) you'll find yourself _not_ using promises, so let's start diving right into them.

---

## Creating a promise

The Promise API exposes a Promise constructor, which you initialize using `new Promise()`:

```js
let done = true

const isItDoneYet = new Promise((resolve, reject) => {
  if (done) {
    const workDone = 'Here is the thing I built'
    resolve(workDone)
  } else {
    const why = 'Still working on something else'
    reject(new Error(why))
  }
})
```

The function passed to `new Promise()` is called the **executor**. JavaScript calls it immediately with the `resolve` and `reject` functions.

Call `resolve()` when the operation succeeds. Call `reject()` with an `Error` when it fails.

The Promise constructor is mainly useful when wrapping an older callback-based API. If an API already returns a promise, use that promise directly.

---

## Consuming a promise

In the last section, we introduced how a promise is created.

Now let's see how the promise can be _consumed_ or used.

Use `.then()` to handle a fulfilled value and `.catch()` to handle a rejection:

```js
isItDoneYet
  .then(value => {
    console.log(value)
  })
  .catch(error => {
    console.error(error)
  })
```

The executor already ran when `isItDoneYet` was created. These methods register handlers for its result.

---

## Chaining promises

A promise can be returned to another promise, creating a chain of promises.

A great example of chaining promises is the [Fetch API](https://flaviocopes.com/fetch-api/).

Calling `fetch()` returns a promise. You don't need to wrap it in `new Promise()`.

### Example of chaining promises

```js
const status = response => {
  if (!response.ok) {
    throw new Error(`HTTP error: ${response.status}`)
  }

  return response
}

const json = response => response.json()

fetch('/todos.json')
  .then(status)
  .then(json)
  .then(data => {
    console.log('Request succeeded with JSON response', data)
  })
  .catch(error => {
    console.log('Request failed', error)
  })
```

In this example, we call `fetch()` to get a list of TODO items from the `todos.json` file found in the domain root, and we create a chain of promises.

When fulfilled, the `fetch()` promise gives us a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response). Here we use:

- `ok`, which is `true` for status codes from 200 through 299
- `status`, the numeric HTTP status code

`response` also has a `json()` method, which returns a promise that will resolve with the content of the body processed and transformed into **[JSON](https://flaviocopes.com/json/)**.

The first handler checks the HTTP status. Throwing an error rejects the promise returned by that `.then()`.

The chain skips the remaining fulfillment handlers and moves to `.catch()`.

If that succeeds instead, it calls the `json()` function we defined. Since the previous promise, when successful, returned the `response` object, we get it as an input to the second promise.

In this case, we return the data JSON processed, so the third promise receives the JSON directly:

```js
.then((data) => {
  console.log('Request succeeded with JSON response', data)
})
```

and we log it to the console.

---

## Handling errors

In the above example, in the previous section, we had a `catch` that was appended to the chain of promises.

When anything in the chain of promises fails and raises an error or rejects the promise, the control goes to the nearest `catch()` statement down the chain.

```js
new Promise((resolve, reject) => {
  throw new Error('Error')
}).catch(err => {
  console.error(err)
})

// or

new Promise((resolve, reject) => {
  reject(new Error('Error'))
}).catch(err => {
  console.error(err)
})
```

### Cascading errors

If you throw an error inside `.catch()`, the promise returned by `.catch()` is rejected. You can append another `.catch()` to handle it:

```js
new Promise((resolve, reject) => {
  throw new Error('Error')
})
  .catch(err => {
    throw new Error('Error')
  })
  .catch(err => {
    console.error(err)
  })
```

---

## Orchestrating promises

### Promise.all()

`Promise.all()` returns a promise that fulfills when every input fulfills. The result keeps the same order as the input.

It rejects as soon as one input rejects.

Example:

```js
const f1 = fetch('/something.json')
const f2 = fetch('/something2.json')

Promise.all([f1, f2])
  .then(res => {
    console.log('Array of results', res)
  })
  .catch(err => {
    console.error(err)
  })
```

The [ES2015 destructuring assignment](https://flaviocopes.com/es6/#destructuring-assignments) syntax allows you to also do

```js
Promise.all([f1, f2]).then(([res1, res2]) => {
  console.log('Results', res1, res2)
})
```

You are not limited to using `fetch` of course, **any promise is good to go**.

### Promise.allSettled()

Use `Promise.allSettled()` when you need every outcome, even if some operations fail:

```js
Promise.allSettled([f1, f2]).then(results => {
  console.log(results)
})
```

Each result has a `status` of `fulfilled` or `rejected`, plus its value or rejection reason.

### Promise.any()

`Promise.any()` fulfills with the first input that fulfills:

```js
Promise.any([f1, f2]).then(result => {
  console.log(result)
})
```

It ignores rejections unless every input rejects. In that case, it rejects with an `AggregateError`.

### Promise.race()

`Promise.race()` settles as soon as the first input settles.

This means the result can be a fulfillment or a rejection. See the [MDN promise concurrency guide](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#promise_concurrency) for a precise comparison.

Example:

```js
const promiseOne = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one')
})
const promiseTwo = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two')
})

Promise.race([promiseOne, promiseTwo]).then(result => {
  console.log(result) // 'two'
})
```

To see the difference between `Promise.all()`, `Promise.race()` and the other combinators on a visual timeline, try the [promise combinators tool](https://flaviocopes.com/tools/promise-combinators/).

## Common errors

### Calling Promise incorrectly

The Promise constructor requires `new` and an executor function:

```js
const promise = new Promise(resolve => {
  resolve('done')
})
```

Calling `Promise()` without `new`, or calling `new Promise()` without an executor, throws a `TypeError`.
