JS

JavaScript Fundamentals

25 lessons

Progress0%
1. Introduction to JavaScript
1What is JavaScript?2Setting Up Your Environment
2. Variables and Data Types
1Declaring Variables2Data Types3Type Conversion
3. Operators
Arithmetic OperatorsComparison OperatorsLogical Operators
4. Control Flow
Conditional StatementsLoops
5. Functions
Function Basics
6. Arrays & Iteration
Array MethodsSpread, Rest & Destructuring
7. Objects & JSON
Working with ObjectsJSON & Optional Chaining
8. OOP & Classes
Class BasicsInheritance & Private Fields
9. Modules & Modern JS
ES ModulesModern JavaScript Features
10. Async JavaScript
PromisesAsync/Await
11. Error Handling
Error Types & try/catchCustom Errors & Debugging
12. Iterators & Advanced
Iterators & GeneratorsMap, Set & WeakRefs
All Tutorials
JavaScriptAsync JavaScript
Lesson 20 of 25 min
Chapter 10 · Lesson 1

Promises

Promises

JavaScript is single-threaded, so long-running operations (network requests, file I/O, timers) are handled asynchronously. Promises are the foundational abstraction for managing asynchronous results.

What Is a Promise?

A Promise is an object that represents an operation that hasn't completed yet but is expected to in the future. It can be in one of three states:

  • Pending — initial state, neither fulfilled nor rejected
  • Fulfilled — the operation completed successfully (value available)
  • Rejected — the operation failed (reason available)

Once a Promise settles (fulfills or rejects), it cannot change state.

Creating a Promise

js
const p = new Promise((resolve, reject) => {
  // async work...
  if (success) resolve(value);
  else         reject(new Error("Something went wrong"));
});

.then / .catch / .finally

Handlers are chained off a Promise:

js
p
  .then((value) => console.log(value))
  .catch((err)  => console.error(err.message))
  .finally(()   => console.log("Done"));

.then can take two arguments: onFulfilled and onRejected. Returning a value from .then wraps it in a new Promise, enabling chaining.

Promise Combinators

MethodDescription
Promise.all(arr)Resolves when all resolve; rejects as soon as any rejects
Promise.race(arr)Settles with the first Promise to settle (either way)
Promise.allSettled(arr)Waits for all to settle; never rejects; returns status/value pairs
Promise.any(arr)Resolves with the first fulfilled Promise; rejects if all reject

Rejection Handling

Unhandled Promise rejections crash Node.js processes and show warnings in browsers. Always attach a .catch or handle rejection in the second argument of .then.

You can also listen globally: process.on("unhandledRejection", handler) in Node.js.

Code Examples

Creating and Chaining Promisesjavascript
function fetchUser(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id > 0) {
        resolve({ id, name: "Alice", role: "admin" });
      } else {
        reject(new Error("Invalid user ID"));
      }
    }, 50);
  });
}

function fetchPermissions(user) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(user.role === "admin" ? ["read", "write", "delete"] : ["read"]);
    }, 50);
  });
}

// Chain: fetch user, then permissions
fetchUser(1)
  .then((user) => {
    console.log("User:", user.name);
    return fetchPermissions(user);  // returning a Promise flattens the chain
  })
  .then((perms) => {
    console.log("Permissions:", perms);
  })
  .catch((err) => {
    console.error("Error:", err.message);
  })
  .finally(() => {
    console.log("Request complete");
  });

Returning a Promise from .then flattens the chain — the next .then waits for the inner Promise to resolve. .catch at the end catches any rejection from any step in the chain.

Promise.all and Promise.allSettledjavascript
const delay = (ms, val) => new Promise((res) => setTimeout(() => res(val), ms));
const fail  = (ms, msg) => new Promise((_, rej) => setTimeout(() => rej(new Error(msg)), ms));

// Promise.all — all must succeed
Promise.all([delay(50, "A"), delay(30, "B"), delay(70, "C")])
  .then((results) => console.log("all:", results))
  .catch((e) => console.error("all failed:", e.message));

// Promise.allSettled — get every outcome
Promise.allSettled([
  delay(40, "ok"),
  fail(20, "bad request"),
  delay(60, "also ok"),
]).then((results) => {
  results.forEach(({ status, value, reason }) => {
    if (status === "fulfilled") console.log("OK:", value);
    else console.log("ERR:", reason.message);
  });
});

Promise.all is best when all results are needed and any failure means the whole operation fails. Promise.allSettled is ideal for independent operations where you want each result regardless of success or failure.

Promise.race and Promise.anyjavascript
const delay = (ms, val) => new Promise((res) => setTimeout(() => res(val), ms));
const fail  = (ms, msg) => new Promise((_, rej) => setTimeout(() => rej(new Error(msg)), ms));

// Promise.race — first to settle wins (even if rejected)
Promise.race([
  delay(100, "slow"),
  delay(30,  "fast"),
  delay(60,  "medium"),
]).then((v) => console.log("race winner:", v));

// Promise.any — first to FULFILL wins; ignores rejections
Promise.any([
  fail(10,  "error 1"),
  delay(50, "second"),
  fail(20,  "error 2"),
]).then((v) => console.log("any winner:", v));

// Promise.any when all reject
Promise.any([
  fail(10, "e1"),
  fail(20, "e2"),
]).catch((e) => console.log("AggregateError:", e.constructor.name));

Promise.race returns the first settled result — useful for timeouts. Promise.any returns the first fulfilled result, skipping rejections — useful for redundant sources. If all reject, it throws an AggregateError.

Quick Quiz

1. What are the three possible states of a Promise?

2. What does Promise.all do if one of the Promises rejects?

3. Which combinator never rejects and always waits for every Promise to settle?

Was this lesson helpful?

PreviousNext