Back to journal
·2 min readJavaScriptPromisesAsync ProgrammingAsynchronousCallbacks

Understanding Promises in JavaScript

Promises in plain language. Pending, fulfilled, rejected, chaining, Promise.all, race, resolve, reject, with pasteable examples.

ShareCopy failed

Async work is most of web dev. Promises are how JavaScript represents "not done yet" without nesting callbacks six levels deep.

A Promise holds a future value. It starts pending, becomes fulfilled with a value, or rejected with an error. Settling means leaving pending for one of those two.

Pending

const promise = new Promise((resolve, reject) => {
  // async work here
  // later: resolve(result) or reject(error)
});

Fulfilled

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Operation succeeded!");
  }, 2000);
});

promise.then((result) => {
  console.log(result);
});

Rejected

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error("Something went wrong!"));
  }, 2000);
});

promise.catch((error) => {
  console.log(error.message);
});

Chaining

Return a Promise from .then() to chain async steps:

const getUser = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({ id: 1, name: "John" });
    }, 2000);
  });
};

const getUserPosts = (user) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(["Post 1", "Post 2"]);
    }, 2000);
  });
};

getUser()
  .then((user) => getUserPosts(user))
  .then((posts) => console.log(posts));

Promise.all

Waits for every Promise in the array. One failure rejects the whole thing.

const fetchUser = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({ id: 1, name: "John" });
    }, 2000);
  });
};

const fetchPosts = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(["Post 1", "Post 2"]);
    }, 1500);
  });
};

const fetchComments = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(["Comment 1", "Comment 2"]);
    }, 1000);
  });
};

Promise.all([fetchUser(), fetchPosts(), fetchComments()])
  .then(([user, posts, comments]) => {
    console.log("User:", user);
    console.log("Posts:", posts);
    console.log("Comments:", comments);
  })
  .catch((error) => {
    console.log("Error:", error);
  });

Promise.race

Settles when the first Promise settles. Handy for timeouts or racing redundant requests.

const fetchResource = (resource, delay) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`${resource} is fetched successfully in ${delay}ms`);
    }, delay);
  });
};

const resource1 = fetchResource("Resource 1", 2000);
const resource2 = fetchResource("Resource 2", 1500);
const resource3 = fetchResource("Resource 3", 1000);

Promise.race([resource1, resource2, resource3])
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
    console.log(error);
  });

Promise.resolve / Promise.reject

Create an already-settled Promise. Useful when an API must return a Promise but your path is sync.

const fetchData = (shouldSucceed) => {
  if (shouldSucceed) {
    return Promise.resolve("Data fetched successfully");
  } else {
    return Promise.reject(new Error("Failed to fetch data"));
  }
};

fetchData(true)
  .then((result) => console.log(result))
  .catch((error) => console.log(error));

fetchData(false)
  .then((result) => console.log(result))
  .catch((error) => console.log(error));

Today

async/await is sugar on top of Promises. You still need to know rejection paths and parallel patterns (all, allSettled). Callback hell was the old pain. Unhandled Promise rejections are the new one.

Learn the states, chain with care, and always attach .catch() (or try/catch in async functions) where failures matter.

ShareCopy failed