All posts
JavaScriptOctober 21, 20267 min read

JavaScript Promises Explained for Beginners (Examples)

JavaScript promises explained for beginners: learn pending, fulfilled and rejected, then(), catch(), finally() and Promise.all with simple runnable examples.

F
Fepiq Team
Fepiq

What is a JavaScript promise? A promise is an object that stands for a value you do not have yet, but will get later. Think of ordering food at a restaurant: you get a receipt now, and the meal arrives later. The receipt is the promise. In this beginner guide you will learn what promises are, how to use then(), catch() and finally(), and how to create your own.

You do not need any experience with promises. If you already know functions and the Fetch API, you are ready. We define every new word as we go.

Why do we need promises?

Some tasks take time, like loading data from a server. JavaScript does not stop and wait. It keeps running the rest of your code. This is called asynchronous code, which just means 'not happening in order, right now'.

Before promises, people used callbacks (functions passed into other functions to run later). Many nested callbacks became hard to read. Promises give us a cleaner way to say: 'do this, and when it finishes, do that'.

The three states of a promise

StateMeaningExample
pendingThe work is still runningWaiting for a server reply
fulfilledThe work finished successfullyData arrived
rejectedThe work failedNo internet connection

A promise starts as pending. It then becomes fulfilled or rejected, and it never changes again. Once it has a result, we say it is settled.

Your first promise

js
const promise = new Promise((resolve, reject) => {
  const ok = true;
  if (ok) {
    resolve('It worked!');
  } else {
    reject('Something went wrong');
  }
});

console.log(promise);

Line 1 creates a new promise. We give it a function with two helpers: resolve and reject. Line 2 makes a simple true/false value. If it is true, we call resolve() with a success message. Otherwise we call reject() with an error message. The last line prints the promise object.

Using the result with then() and catch()

js
promise
  .then((message) => {
    console.log('Success:', message);
  })
  .catch((error) => {
    console.log('Error:', error);
  });

The then() method runs when the promise is fulfilled, and it receives the value passed to resolve(). The catch() method runs when the promise is rejected, and it receives the value passed to reject(). With ok set to true, you will see: Success: It worked!

A realistic example: a delay

js
function wait(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

wait(2000).then(() => {
  console.log('Two seconds have passed');
});

The wait function returns a promise. Inside, setTimeout calls resolve after the number of milliseconds you give (1000 ms is one second). We call wait(2000) and use then() to run code after two seconds. Notice that we did not need reject here because nothing can fail.

Chaining promises

Each then() returns a new promise, so you can link steps one after another. This is called chaining.

js
Promise.resolve(5)
  .then((n) => n * 2)
  .then((n) => n + 1)
  .then((n) => {
    console.log(n); // 11
  });

Promise.resolve(5) makes a promise that is already fulfilled with 5. The first then() doubles it to 10. Whatever a then() returns is passed to the next one, so the second adds 1 to make 11. The last step prints 11.

finally(): code that always runs

js
fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then((response) => response.json())
  .then((todo) => console.log(todo.title))
  .catch((error) => console.log('Failed:', error))
  .finally(() => console.log('Done'));

fetch() returns a promise. The first then() turns the reply into JSON, which is also a promise. The second then() prints the title. If anything fails, catch() handles it. The finally() method runs at the end whether it worked or not, which is perfect for hiding a loading message.

Running many promises together

js
const a = wait(1000).then(() => 'A');
const b = wait(2000).then(() => 'B');

Promise.all([a, b]).then((results) => {
  console.log(results); // ['A', 'B']
});

Promise.all takes a list of promises and waits for all of them. It gives back an array of results in the same order. Both timers run at the same time, so it takes about 2 seconds, not 3. If one promise fails, Promise.all fails too.

Promises vs async/await

The async/await syntax is just a friendlier way to write promises. Under the hood it uses the same thing. Here is the delay example written both ways.

js
// with then()
wait(1000).then(() => console.log('Hi'));

// with async/await
async function run() {
  await wait(1000);
  console.log('Hi');
}
run();

The await keyword pauses inside the async function until the promise settles, then gives you the value. Learning promises first makes async/await much easier to understand.

Common beginner mistakes

  • Forgetting to return a value inside then(), so the next step gets undefined.
  • Forgetting catch(), so errors go unnoticed.
  • Using the promise like a normal value, for example logging it right away and seeing Promise { <pending> }.
  • Calling resolve() and reject() both. Only the first call counts.

Frequently asked questions

What is a promise in JavaScript in simple words?+

A promise is an object that represents a result that will arrive later. It can end up fulfilled with a value or rejected with an error. You use then() and catch() to react to either outcome.

What is the difference between a promise and a callback?+

A callback is a function you pass in to be run later. A promise is an object you get back that you can attach steps to. Promises are easier to chain and handle errors in, so they avoid deeply nested code.

What does resolve and reject mean in a promise?+

resolve() marks the promise as successful and passes along a value. reject() marks it as failed and passes along an error. Only the first one you call has any effect.

Is async/await better than promises?+

They are the same thing underneath. Async/await reads more like normal top-to-bottom code, so many beginners prefer it. You still need to understand promises to use it well.

What happens if I do not use catch()?+

If a promise is rejected and nothing handles it, you get an unhandled rejection error in the console. Always add catch() or use try/catch with await so failures are handled.

Ready for the easier syntax? Continue the path with our async/await guide.

Read async/await for beginners

Want help building a web app or learning to code with guidance? Talk to the Fepiq team.

Contact Fepiq

Get new posts in your inbox

Occasional, no-fluff notes on shipping modern software — startups, automation, Laravel, Shopify and more. No spam, unsubscribe anytime.

Keep reading

Related posts

All posts

Let's build something

Ready to ship your next product with Fepiq?

Book a free discovery call. We'll listen, ask sharp questions, and send you a proposal within 3 business days.