W3docs

JavaScript Promises

Learn JavaScript Promises: the three states, executor function with resolve/reject, then/catch/finally, and microtask timing.

Promises in JavaScript are a powerful tool for managing asynchronous operations, enabling developers to write cleaner, more robust code. This chapter explains what a promise is, the three states it can be in, how the executor function works with resolve and reject, how to consume results with .then, .catch, and .finally, and when promise callbacks actually run (the microtask queue). Understanding these fundamentals is essential before moving on to chaining, the Promise API, and async/await.

Introduction to JavaScript Promises

A Promise is an object that represents a value that may not be available yet, but will be at some point in the future. Instead of passing a callback into an asynchronous function and hoping it gets called, you receive a promise object immediately and attach callbacks to it. This avoids deeply nested callbacks, often called "callback hell," and gives you a single, consistent way to handle both success and failure.

A typical asynchronous task — a network request, a timer, reading a file — does not have its result ready right away. The promise is a placeholder for that result.

The Three States of a Promise

A promise is always in exactly one of three states:

  • pending — the initial state; the operation has not completed yet.
  • fulfilled — the operation completed successfully, and the promise has a value.
  • rejected — the operation failed, and the promise has a reason (usually an Error).

A pending promise can transition to either fulfilled or rejected. Once it does, it is settled and can never change state again. This one-way, one-time transition is what makes promises predictable: a .then callback attached to an already-fulfilled promise will still run, and a promise can never flip from fulfilled back to rejected.

                  ┌─────────────┐  resolve(value)   ┌─────────────┐
                  │   pending   │ ────────────────▶ │  fulfilled  │
   new Promise ─▶ │             │                   └─────────────┘
                  │             │  reject(reason)   ┌─────────────┐
                  └─────────────┘ ────────────────▶ │  rejected   │
                                                    └─────────────┘

Creating a Promise

To create a promise, you call the Promise constructor and pass it a function called the executor. The executor runs immediately and synchronously the moment the promise is created. It receives two functions as arguments, conventionally named resolve and reject:

  • Call resolve(value) to fulfill the promise with value.
  • Call reject(reason) to reject the promise with reason.

Until you call one of them, the promise stays pending.


javascript— editable

The executor receives resolve and reject so it can settle the promise once the asynchronous work finishes. Here the promise becomes fulfilled after a one-second timer:


javascript— editable

Note: Only the first call to resolve or reject matters. Once a promise is settled, any further calls to resolve or reject are ignored. Also, if the executor throws an error synchronously, the promise is automatically rejected with that error.

Handling Outcomes with .then, .catch, and .finally

Once a promise has been created, you consume its result with the .then, .catch, and .finally methods. These are how the rest of your code reacts to a settled promise.

The then method

The .then method is used to schedule a callback to be executed when the promise is fulfilled. In order for a promise to be fulfilled, the resolve method should be called. The argument you pass to the resolve method will be the final value for the promise.


javascript— editable

In this code, the promise will be fulfilled only after the 1000 ms timeout is done, and the resolve method is called with "Done!".

The function in the then part is only executed after the resolve method is called.

.then can also take a second argument — a rejection handler — but using a separate .catch (below) is clearer and catches errors from earlier handlers too.

The .catch method

The .catch method is used to handle the promise if it gets rejected. It means either an error is thrown in the promise function block or the reject method is called.


javascript— editable

For richer error-handling patterns — rejection vs. thrown errors, re-throwing, and recovering inside a chain — see Error Handling with Promises.

The .finally method

The .finally method allows you to execute code after the promise is settled, regardless of its outcome. It receives no arguments (it does not know whether the promise fulfilled or rejected) and passes the result or error through unchanged, so it is ideal for cleanup such as hiding a loading spinner.


javascript— editable

When Do Promise Callbacks Run? (Microtasks)

A common surprise is that .then, .catch, and .finally callbacks never run synchronously, even if the promise is already settled. They are scheduled on the microtask queue, which the engine processes only after the current synchronous code finishes.

This means synchronous code always runs first, and promise callbacks run before timers (setTimeout), which sit on the separate macrotask queue.


javascript— editable

Even though the promise is resolved immediately and the timeout is 0, the promise callback (3) runs before the timeout callback (4), because the microtask queue is fully drained before the next macrotask runs.

Fetching Data from an API using promises

This example shows how to fetch data from a remote API using promises.


javascript— editable

Chaining Promises

Promise chaining is a powerful feature that allows you to link multiple asynchronous operations together. Each .then returns a new promise, and whatever you return from a handler becomes the fulfillment value of that new promise — which is why the example below carries a value through several steps. For more information, check JavaScript: Promises and Chaining.


javascript— editable

Integrating async/await with JavaScript Promises

Using async/await effectively can simplify the handling of asynchronous operations, making your code cleaner and easier to understand while maintaining all the power of JavaScript promises. You will learn more about it in JavaScript async/await, but here is a simple example.


javascript— editable

Conclusion

Mastering JavaScript promises is crucial for any developer looking to manage asynchronous operations efficiently. Remember the core model: a promise is pending until the executor calls resolve or reject, after which it is settled forever; you read the result with .then/.catch/.finally; and those callbacks always run as microtasks, after the current synchronous code.

Where to go next

Practice

Practice
What is a promise in JavaScript?
What is a promise in JavaScript?
Was this page helpful?