Back to JavaScript Notes
Topic #413

Promises

By the end of this lesson, you will be able to create, chain, and handle JavaScript Promises so asynchronous code reads as a sequence of eventual results instead of nested callbacks.

What it is

A Promise is an object that represents a value that may not be available yet. It is a cleaner model for eventual results: instead of passing a callback into every asynchronous function, the function returns a promise that can later become fulfilled with a value or rejected with an error.

A promise has three states: pending, fulfilled, and rejected. Once it is fulfilled or rejected, it is settled and cannot change state. Related terms include then(), catch(), finally(), async/await, and the promise executor function passed to new Promise().

Why it matters

  • It flattens callback nesting, making asynchronous flows easier to read.
  • It gives a standard return type for asynchronous APIs, such as fetch().
  • It separates success handling from error handling through chaining.
  • It enables composition with helpers like Promise.all() and Promise.race().
  • It is the foundation for async/await, which lets you write promise-based code in a synchronous style.

Syntax or steps

The smallest useful pattern is to return a promise and attach handlers:

const promise = new Promise((resolve, reject) => {
  // asynchronous work
  resolve(value);
  // or reject(error);
});

promise
  .then((value) => {
    // success
  })
  .catch((error) => {
    // failure
  });

Each then() returns a new promise, so you can chain transformations. If a handler throws an error or returns a rejected promise, the chain jumps to the nearest catch().

For network code, improve the reference example by checking the response and handling errors:

fetch(url)
  .then((response) => {
    if (!response.ok) throw new Error('HTTP error');
    return response.json();
  })
  .then((data) => console.log(data))
  .catch((error) => console.error(error));

Example

const getUser = (id) => new Promise((resolve, reject) => {
  setTimeout(() => {
    if (id > 0) {
      resolve({ id, name: 'Ada' });
    } else {
      reject(new Error('Invalid user id'));
    }
  }, 500);
});

getUser(1)
  .then((user) => {
    console.log('Loaded:', user);
    return user.name;
  })
  .then((name) => {
    console.log('Hello,', name);
  })
  .catch((error) => {
    console.error('Failed:', error.message);
  })
  .finally(() => {
    console.log('Request finished');
  });

Part by part:

  • getUser() returns a promise. The executor runs immediately, while setTimeout() simulates asynchronous work.
  • resolve() fulfills the promise with the user object; reject() rejects it with an error.
  • The first then() receives the fulfilled value and returns user.name.
  • The second then() receives that returned value because promises chain.
  • catch() handles rejection or any error thrown inside a then().
  • finally() runs after the chain settles, regardless of success or failure.

Common mistakes

  • Forgetting to return a value from then(). If you do not return the next value, the following then() receives undefined.
  • Not handling rejection. A rejected promise without catch() can cause an unhandled rejection. Add catch() at the end of the chain.
  • Assuming then() runs immediately. Promise handlers run asynchronously after the current call stack is empty, even if the promise is already settled.
  • Using async functions without awaiting their promises. Calling an async function returns a promise; use await or then() to get the result.

When to use it

Promises are best when you need a composable asynchronous result. Compare them with the closest alternatives:

ApproachUse whenTrade-off
CallbacksWorking with older APIs that require callback functions.Can lead to nesting and inconsistent error handling.
PromisesYou need chaining, composition, or a standard async return value.Still uses then() callbacks, though flatter than nested callbacks.
async/awaitYou want promise-based code to read like synchronous code.Requires an async function and can be less convenient for simple one-step chains.

Practice

Guided exercise: Write a function delay(ms) that returns a promise resolving after ms milliseconds. Use it with then() to log a message after one second.

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

delay(1000).then(() => console.log('One second passed'));

Challenge: Modify delay() so it rejects if ms is negative, then handle the error with catch().

Expected output for delay(-1): an error message such as Invalid delay.

Quick check

Question: What happens if a then() handler throws an error?

Answer: The promise returned by that then() becomes rejected, and the chain continues to the nearest catch() handler.

Summary

A promise is a stand-in for a future value, with clear states and composable handlers. Use promises when asynchronous work needs to be chained, composed, or standardized. Once comfortable, async/await can make the same model easier to read.

Want to go beyond the notes?

Join Coding Now Tech Institute's JavaScript course — live mentorship, real projects, and 100% placement support.

Enroll Now — Free Demo Available

Promises – FAQs

Quick answers about learning Promises in JavaScript.

This free note from Coding Now Tech Institute explains Promises in JavaScript — concept, syntax and worked code examples you can copy, run and revise before interviews.
Yes. Every JavaScript topic on Coding Now Tech Institute, including Promises, is 100% free with no signup required.
With focused practice, most students grasp Promises in 1–3 days from these notes; pairing it with Coding Now Tech Institute's mentor-led course takes you to job-ready depth faster.
Use the code examples in this note, then ask doubts for free on the Coding Now Tech Institute Community (/community) — expert instructors answer within 24 hours.
Call NowEnroll Now