DevPrep
  • Interview Prep
  • Projects
  • Resources
  • Pricing
  • About Us
Submit Question
DevPrep
  • Pricing
  • About Us
Submit Question
  1. Home
  2. Articles
  3. Frontend Engineering
  4. Explain Promises to a 5-Year-Old — Then Use Them Like a Pro
XLinkedInReddit
MediumFrontend Engineering

Explain Promises to a 5-Year-Old — Then Use Them Like a Pro

D
DevPrep Team
February 9, 2026·3 min read·0
Table of Contents
  • The 5-Year-Old Explanation
  • The Real Technical Version
  • Promise Chaining — The Power Feature
  • Promise Static Methods — The Toolkit
  • Promise.all — All or Nothing
  • Promise.allSettled — Get All Results
  • Promise.race — First One Wins
  • Real Production Patterns
  • Pattern 1: Retry with Exponential Backoff
  • Pattern 2: Sequential Promise Execution
  • Production Issues
  • Issue 1: Unhandled Promise Rejections
  • Issue 2: Promise vs Callback Confusion
  • Best Practices

Written by Rahul · Frontend Engineer at Google · Updated 2025

The 5-Year-Old Explanation

Imagine you go to a pizza shop. You order a pizza. The shop gives you a receipt (that's the Promise). The pizza isn't ready yet, but the receipt promises you'll get it.

Three things can happen:

  • 🍕 Fulfilled — Your pizza is ready! (.then())
  • ❌ Rejected — They're out of cheese. Sorry! (.catch())
  • ⏳ Pending — Still in the oven...

You don't stand at the counter waiting. You go sit down (the code keeps running), and they'll call your name when it's done (the callback runs).

The Real Technical Version

// Creating a Promise
const pizzaOrder = new Promise((resolve, reject) => {
  const hasCheese = true;

  setTimeout(() => {
    if (hasCheese) {
      resolve({ type: "Margherita", size: "Large" });
    } else {
      reject(new Error("Out of cheese!"));
    }
  }, 2000);
});

// Consuming the Promise
pizzaOrder
  .then(pizza => console.log("Got pizza:", pizza))
  .catch(error => console.error("Order failed:", error))
  .finally(() => console.log("Transaction complete"));

Promise Chaining — The Power Feature

fetch("/api/user/123")
  .then(response => {
    if (!response.ok) throw new Error("User not found");
    return response.json();  // Returns a new Promise
  })
  .then(user => {
    return fetch(`/api/posts?userId=${user.id}`);  // Chain another fetch
  })
  .then(response => response.json())
  .then(posts => {
    console.log("User posts:", posts);
  })
  .catch(error => {
    // Catches ANY error in the chain
    console.error("Something went wrong:", error);
  });

Promise Static Methods — The Toolkit

Promise.all — All or Nothing

// Fetch multiple resources in parallel
const [users, posts, comments] = await Promise.all([
  fetch("/api/users").then(r => r.json()),
  fetch("/api/posts").then(r => r.json()),
  fetch("/api/comments").then(r => r.json()),
]);

// If ANY one fails, the whole thing rejects
// Use case: Dashboard that needs all data to render

Promise.allSettled — Get All Results

const results = await Promise.allSettled([
  fetch("/api/critical"),
  fetch("/api/optional"),
  fetch("/api/nice-to-have"),
]);

results.forEach(result => {
  if (result.status === "fulfilled") {
    console.log("Success:", result.value);
  } else {
    console.log("Failed:", result.reason);
  }
});
// Use case: When some failures are acceptable

Promise.race — First One Wins

// Implement a timeout for fetch
const fetchWithTimeout = (url, timeout = 5000) => {
  return Promise.race([
    fetch(url),
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error("Request timed out")), timeout)
    )
  ]);
};

// Use case: Race between fetch and timeout
const data = await fetchWithTimeout("/api/slow-endpoint", 3000);

Real Production Patterns

Pattern 1: Retry with Exponential Backoff

async function fetchWithRetry(url, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url);
      if (response.ok) return await response.json();
      throw new Error(`HTTP ${response.status}`);
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
      console.log(`Retry ${i + 1} in ${delay}ms...`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

Pattern 2: Sequential Promise Execution

// Process items one at a time (not in parallel)
async function processSequentially(items) {
  const results = [];
  for (const item of items) {
    const result = await processItem(item); // Wait for each one
    results.push(result);
  }
  return results;
}

// Process with concurrency limit
async function processWithLimit(items, limit = 3) {
  const results = [];
  const executing = new Set();

  for (const item of items) {
    const promise = processItem(item).then(result => {
      executing.delete(promise);
      return result;
    });
    executing.add(promise);
    results.push(promise);

    if (executing.size >= limit) {
      await Promise.race(executing);
    }
  }
  return Promise.all(results);
}

Production Issues

Issue 1: Unhandled Promise Rejections

// ❌ This silently fails in production
fetch("/api/data").then(r => r.json());

// ✅ Always handle errors
fetch("/api/data")
  .then(r => r.json())
  .catch(err => showErrorToUser(err));

// Node.js will crash on unhandled rejections (since Node 15)
process.on("unhandledRejection", (reason) => {
  console.error("Unhandled:", reason);
  process.exit(1);
});

Issue 2: Promise vs Callback Confusion

// ❌ Mixing callbacks and promises
function getData(callback) {
  fetch("/api")
    .then(r => r.json())
    .then(data => callback(null, data))
    .catch(err => callback(err));
}

// ✅ Just return the promise
async function getData() {
  const response = await fetch("/api");
  return response.json();
}

Best Practices

  1. Always catch errors — every Promise chain needs a .catch() or try/catch
  2. Use async/await for readability — but understand Promises underneath
  3. Use Promise.all for parallel operations — don't await sequentially when you can parallelize
  4. Use Promise.allSettled when partial failure is acceptable
  5. Add timeouts to all network requests with Promise.race

Related Articles

MediumFrontend Engineering

System Design #12: Design a Multi-Step Form Wizard

7 min read
MediumFrontend Engineering

Mastering Senior-Level JavaScript Interview Concepts

2 min read
MediumFrontend Engineering

System Design #9: Design a Collaborative Text Editor

9 min read

Comments (0)

Sign in to leave a comment.

No comments yet. Be the first to comment.

Table of Contents

  • The 5-Year-Old Explanation
  • The Real Technical Version
  • Promise Chaining — The Power Feature
  • Promise Static Methods — The Toolkit
  • Promise.all — All or Nothing
  • Promise.allSettled — Get All Results
  • Promise.race — First One Wins
  • Real Production Patterns
  • Pattern 1: Retry with Exponential Backoff
  • Pattern 2: Sequential Promise Execution
  • Production Issues
  • Issue 1: Unhandled Promise Rejections
  • Issue 2: Promise vs Callback Confusion
  • Best Practices

Series

View all Frontend Engineering articles →

Practice

  • JavaScript
  • DSA
  • Machine Coding
  • System Design

Resources

  • Learning Tracks
  • Articles
  • Roadmaps
  • Compare Concepts
  • Glossary
  • Developer Tools
  • All Questions

Company

  • About
  • Pricing

Legal

  • Privacy Policy
  • Terms of Service
DevPrep

© 2026 DevPrep. All rights reserved.