CodingNic

Modern JavaScript for Node.js

Promises

Modern JavaScript for Node.js 12 min read

Promises

Objectives

By the end of this lesson, you should be able to:

  • Explain what a promise represents and its three possible states
  • Chain .then(), .catch(), and .finally() correctly
  • Run several promises at once with Promise.all()

💡 Why this matters: Almost everything a backend does, querying a database, reading a file, calling another API, takes time and doesn’t have an answer immediately. Promises are JavaScript’s way of representing “this value isn’t ready yet, but here’s what to do once it is.”

⚠️ A note on verification: every snippet and output in this lesson was actually run with Node.js.

What a Promise Represents

A promise is an object representing a value that will exist eventually, either successfully (resolved) or unsuccessfully (rejected). It starts pending, and settles into exactly one of those two outcomes, never both, and never more than once.

javascript
function fetchUser(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id <= 0) {
        reject(new Error("invalid id"));
      } else {
        resolve({ id, name: "Erin Castillo" });
      }
    }, 10);
  });
}

fetchUser(1).then((user) => {
  console.log("resolved:", user);
});
text
resolved: { id: 1, name: 'Erin Castillo' }

new Promise((resolve, reject) => {...}) wraps asynchronous work (here, setTimeout standing in for something slow, like a real database query). Calling resolve(value) settles the promise successfully, reject(error) settles it as a failure. .then(callback) runs once the promise resolves, receiving the resolved value.

Chaining

javascript
fetchUser(1)
  .then((user) => {
    console.log("first:", user.name);
    return fetchUser(2);
  })
  .then((user) => {
    console.log("second:", user.name);
  })
  .catch((err) => {
    console.log("error:", err.message);
  })
  .finally(() => {
    console.log("chain finished");
  });
text
first: Erin Castillo
second: Erin Castillo
chain finished

Returning a new promise from inside .then() chains it, the next .then() waits for that returned promise too, rather than running immediately. .catch() handles a rejection anywhere earlier in the chain, .finally() runs regardless of whether the chain succeeded or failed, the same three-way pattern as try/catch/finally from the previous lesson.

Handling Rejection

javascript
fetchUser(-1)
  .then((user) => console.log("resolved:", user))
  .catch((err) => console.log("caught:", err.message));
text
caught: invalid id

fetchUser(-1) rejects (id is invalid), so the .then() callback never runs at all, execution jumps straight to .catch().

Running Several Promises at Once

javascript
function fetchUser(id) {
  return new Promise((resolve) => {
    setTimeout(() => resolve({ id, name: `User ${id}` }), 10);
  });
}

Promise.all([fetchUser(1), fetchUser(2), fetchUser(3)]).then((users) => {
  console.log(users.map((u) => u.name));
});
text
[ 'User 1', 'User 2', 'User 3' ]

Promise.all([...]) waits for every promise in the array to resolve, then resolves itself with an array of all the results, in the same order they were passed in, not necessarily the order they finished. This is far faster than awaiting each one sequentially when they don’t depend on each other, all three fetchUser calls here run concurrently, not one after another.

Try It

  1. Write a function checkStock(productId) returning a promise that resolves with { inStock: true } after a short setTimeout, and call it with .then().
  2. Chain a second .then() onto question 1 that logs a message based on the resolved value.
  3. Write a version of checkStock that rejects if productId is negative, and handle that rejection with .catch().
  4. Use Promise.all() to run three of your checkStock calls at once, and log all three results together.

Recap

  • A promise represents a value that isn’t ready yet, starting pending, settling into either resolved or rejected.
  • .then() handles success, .catch() handles failure, .finally() runs either way, and returning a promise from .then() chains it.
  • Promise.all([...]) runs multiple promises concurrently, resolving once all of them have, in their original order.

Next lesson: async/await, writing asynchronous code that reads like synchronous code.