Skip to main content

Command Palette

Search for a command to run...

JavaScript Promises Explained: A Beginner's Guide to Future Values

Updated
4 min read
JavaScript Promises Explained: A Beginner's Guide to Future Values
A

Focused on building robust web applications and understanding the underlying infrastructure of the internet.

Imagine you go to a burger joint. You place your order, and the cashier gives you a small plastic buzzer. At that moment, you don’t have your burger yet, but you have a promise that you will get one.

While the kitchen is busy, you can go find a seat, check your phone, or talk to a friend. The buzzer represents a value that will be available in the future. Eventually, one of two things will happen:

  1. Success: The buzzer glows, and you get your burger.

  2. Failure: The cashier tells you they ran out of ingredients, and you get a refund.

In JavaScript, Promises work exactly like that buzzer. They allow us to handle asynchronous tasks (like fetching data or uploading a file) without stopping the rest of our code from running.

1. The Problem: Life Before Promises

Before Promises were introduced in 2015 (ES6), developers used Callbacks to handle asynchronous code. If you had to perform several tasks in a row, your code ended up looking like a "pyramid of doom," also known as Callback Hell.

The issues with Callbacks:

  • Readability: The code grows horizontally, making it hard to follow.

  • Inversion of Control: You trust another function to call your callback at the right time, which can lead to bugs.

  • Error Handling: Managing errors in nested callbacks is a nightmare.

2. What is a Promise?

A Promise is a proxy for a value not necessarily known when the promise is created. It is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value.

The Three States of a Promise

A promise is always in one of these three states:

State Meaning Analogy
Pending The initial state; the task is still running. The kitchen is still cooking your burger.
Fulfilled The operation completed successfully. The burger is ready!
Rejected The operation failed. The kitchen ran out of meat.

3. The Promise Lifecycle: Creating vs. Consuming

Step 1: Creating a Promise

You create a promise using the new Promise constructor. It takes a function (called the "executor") which has two arguments: resolve and reject.

const burgerPromise = new Promise((resolve, reject) => {
  let isKitchenStocked = true;

  if (isKitchenStocked) {
    resolve("Delicious Burger 🍔"); // Task Successful
  } else {
    reject("Out of stock ❌"); // Task Failed
  }
});

Step 2: Consuming a Promise

Once a promise is created, we need to handle the result using .then() for success and .catch() for errors.

burgerPromise
  .then((data) => {
    console.log("Success: " + data);
  })
  .catch((error) => {
    console.log("Error: " + error);
  })
  .finally(() => {
    console.log("Process finished."); // Runs no matter what
  });

4. Promise Chaining: The Clean Solution

The most powerful feature of Promises is Chaining. Instead of nesting functions inside each other, we can return a new promise from a .then() block and "chain" another .then() after it. This keeps the code flat and readable.

Example:

fetchUser(1)
  .then(user => fetchPosts(user.id))
  .then(posts => fetchComments(posts[0].id))
  .then(comments => console.log(comments))
  .catch(err => console.error(err));

Instead of moving 10 inches to the right (like Callbacks), your code stays vertically aligned.

5. Why Promises are Better

Feature Callbacks Promises
Structure Nested (Pyramid) Linear (Chained)
Error Handling Done in each function Single .catch() for the whole chain
Readability Poor / Confusing High / Clean
Value Passed as an argument Returns a "Future Object"

Summary

  • Future Value: A promise represents a value that isn't ready yet but will be eventually.

  • States: Remember Pending, Fulfilled, and Rejected.

  • .then(): Use this to handle the successful result.

  • .catch(): Use this to handle any errors in the entire chain.

  • Chaining: Use it to perform multiple asynchronous steps in a clean, vertical order.

Mastering Promises is the key to understanding how modern JavaScript works under the hood. It’s the foundation for even cleaner tools like async/await.

More from this blog

T

Terminal Thoughts

53 posts

A developer’s log of experiments, errors, and breakthroughs. Terminal and Thoughts captures my evolution through a web development cohort, featuring deep dives into the modern tools and technologies.