JAVASCRIPT / LESSON 17

Handle a future result with promises

Follow pending, fulfilled, and rejected results through a chain.

Content updated

By the end, you can…

  • A promise represents a future result. Handle both fulfillment and rejection.
  • Predict the example's result, change one part, and verify the behavior in your own script.
Before you begin

One idea at a time

A promise is an object representing an operation's eventual result. It starts pending, then settles as fulfilled with a value or rejected with a reason. Once settled it does not change to the other outcome. A promise is not the result value itself, and creating one does not automatically move CPU-heavy work off the main thread.

Use then to describe what to do with a fulfilled value, and catch to handle rejection. Each then returns another promise, allowing a chain of steps. Returning a value from a handler gives that value to the next handler. Returning a promise makes the chain wait for that promise's settlement.

What to remember first

Remember first

A promise represents a future result. Handle both fulfillment and rejection.

Read the code before trying it

Promise.resolve(value) / Promise.reject(error)Create a settled result
Promise.resolve(3) gives a fulfilled promise containing 3. Promise.reject(new Error("Sample failure")) gives a rejected promise. Their handlers still run later; no timer is required.
new Error(message)Describe a failure
An Error object carries a message. Passing it to reject rejects the promise; throwing it inside a then handler rejects the promise returned by that handler.
new Promise((resolve, reject) => { ... })Adapt a callback operation
The executor is the function passed into new Promise; it runs immediately. resolve and reject are functions supplied to it by Promise. Call resolve with success or reject with a failure; most browser APIs already supply promises.
promise.then(handler)Use a fulfilled value
The handler runs asynchronously after current synchronous work, even for an already fulfilled promise.
promise.catch(handler)Handle a rejection
It catches rejection from the preceding chain, including exceptions thrown by its handlers.
promise.finally(handler)Perform cleanup
This handler runs for either outcome. Use it to restore UI state, not to obtain the result value.
Promise.all(promises)Wait for several results
It fulfills with results in input order, or rejects when one input rejects. It does not cancel the other operations.

Walk through an example

javascript
const result = new Promise((resolve) => {
  setTimeout(() => resolve(3), 200);
});
console.log("Waiting");
result
  .then((count) => count * 2)
  .then((doubled) => console.log(doubled))
  .catch(() => console.log("Could not finish"));

The executor schedules a timer. Waiting logs before the promise settles. When resolve receives 3, the first then returns 6, and the second logs it. Returning from the first handler connects the two transformations. Without that return, the next handler would receive undefined.

See it happen: try a small change

Handle a future result with promises: try it

Predict the result, choose a version, then compare the code and output. Try the controls when the example has them. Each version starts fresh.

Code to notice

console.log("Waiting");
Promise.resolve(3)
  .then((count) => count * 2)
  .then((value) => console.log(value))
  .finally(() => console.log("Finished"));

Live result

Lesson example
index.htmlPreview

What changed: The chain transforms three to six, then runs cleanup.

Look a little closer

Reject with an Error object when your own operation fails so its reason is meaningful during debugging. Catch near the place that can choose a useful response. A catch handler that returns normally turns the following chain into a fulfilled result; rethrow when later code still needs to treat it as a failure.

Promise.all is useful when independent operations can be started together. Its result order follows the input array rather than completion order. It does not itself start work described as functions: pass the promises returned by calling those operations. Avoid starting dependent work early when it requires a result that is not available yet.

Try it in your own project

Run fulfillment and rejection as separate scripts. Keep the handler that returns the doubled number before the handler that logs it. Add finally and verify cleanup in both cases; then try the supplied Promise.all example separately.

The interactive preview runs in its own isolated document. Its Output panel mirrors console.log messages; it is not a full DevTools console. Choosing another version or resetting starts a fresh example and does not edit your local files. Read any simulation label: storage and request demonstrations explain their offline substitutes, while the written local examples use the real browser APIs.

A common wrong turn

Logging a promise does not unwrap its future value. Use then or, in the next lesson, await. Always handle expected failure; an unhandled rejection can leave your UI stuck in a loading state. Also do not nest then inside then unnecessarily when returning a promise would make a readable chain.

Your turn: handle a future result with promises

Build a delayed promise that either returns a count or fails, then handle both outcomes.

Try this in your own HTML and JavaScript files, then open the page in a browser. There is no separate checked practice for this task yet.

  1. Resolve with 3 after a short timer and return count * 2 from the first then.
  2. Switch the operation to reject with an Error and confirm catch shows a helpful message.
  3. Add finally to log cleanup in both cases. Use Promise.all with two resolved values and check their input order.

Go to the source

Examples are authored for Tuklas and checked with code and reference documentation. This is not a claim of independent human review. Suggest a correction.