Riad Kilani
  • Bio
  • Portfolio
  • Blog
  • Contact
  • Accessibility
  • Case Studies
  • CSS
  • Design
  • Front-End
  • HTML
  • JavaScript
  • News
  • Productivity
  • Random Thoughts
  • SEO
  • Themes
  • Trends
  • Tutorials
  • TypeScript
  • TypeSCript From The Ground Up
  • UX Engineering
  • Web Development
  • Wordpress
Home » JavaScript » JavaScript Promise: What It Is and How to Use It

JavaScript Promise: What It Is and How to Use It

August 15, 2025
Stylized banner illustrating JavaScript Promises with a flow from pending to fulfilled/rejected and labels for then/catch/finally and async/await on a dark code-themed background

A JavaScript Promise is an IOU for a value you’ll get later—perfect for API calls, timers, and other async work. In this quick guide, you’ll learn what a javascript promise is, how it behaves, and simple ways to use it in real projects.

New to fundamentals too? Check out my post on JavaScript scope and this beginner-friendly guide to Emmet productivity tips.


What is a JavaScript Promise?

A JavaScript Promise has three states: pending, fulfilled, and rejected. Instead of blocking the page, you attach handlers that run when the result arrives or fails. (Great background reading: MDN on Promises.)

const fetchUser = (id) =>
  new Promise((resolve, reject) => {
    setTimeout(() => id ? resolve({ id, name: "Riad" }) : reject(new Error("No ID")), 300);
  });

How to use a JavaScript Promise with then/catch/finally

fetchUser(1)
  .then(user => console.log("User:", user))    // success
  .catch(err => console.error("Error:", err))  // failure
  .finally(() => console.log("Done"));         // always

Use this when you want quick chaining or you’re not inside an async function. See also MDN’s guide to then() and catch().


Async/Await: a friendlier way to work with JavaScript Promises

async function load() {
  try {
    const user = await fetchUser(1);
    console.log(user.name);
  } catch (e) {
    console.error(e.message);
  }
}
load();

Reads top-to-bottom like normal code but stays non-blocking. More at MDN: async/await.


Run Promises in parallel with Promise.all

const p1 = fetchUser(1);
const p2 = fetchUser(2);

const [u1, u2] = await Promise.all([p1, p2]);
console.log(u1, u2);

For other patterns, see MDN on Promise.all, allSettled, race, any.


JavaScript Promise Common pitfalls (and quick fixes)

  • Always add a .catch() or wrap await in try/catch to avoid unhandled rejections.
  • In .then() chains, return the next value/Promise so the chain receives it.
  • Avoid mixing callbacks with Promises—wrap callbacks or use APIs that already return Promises.

Real-world: fetch with timeout (AbortController)

Network calls are where a JavaScript Promise shines. Add a timeout so the UI never hangs:

async function fetchJSON(url, { timeout = 5000, signal } = {}) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeout);

  // allow an external signal to abort too
  if (signal) signal.addEventListener('abort', () => controller.abort());

  try {
    const res = await fetch(url, { signal: controller.signal });
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return await res.json();
  } finally {
    clearTimeout(timer);
  }
}

// Usage
fetchJSON('/api/users', { timeout: 3000 })
  .then(data => console.log(data))
  .catch(err => console.error('Request failed:', err.message));

Why this helps: you control failure cases explicitly instead of waiting forever.


Retry with simple backoff (robustifying a JavaScript Promise)

Transient failures happen. Wrap calls in a tiny retry helper:

async function retry(fn, { tries = 3, delay = 400 } = {}) {
  let lastErr;
  for (let i = 1; i <= tries; i++) {
    try { return await fn(); }
    catch (err) {
      lastErr = err;
      if (i < tries) await new Promise(r => setTimeout(r, delay * i)); // linear backoff
    }
  }
  throw lastErr;
}

// Usage
retry(() => fetchJSON('/api/profile')).then(renderProfile).catch(showError);

Microtasks vs macrotasks (why .then feels “so fast”)

.then callbacks run in the microtask queue, which flushes before timers/DOM events. This explains some ordering surprises:

console.log('A');
Promise.resolve().then(() => console.log('B (microtask)'));
setTimeout(() => console.log('C (macrotask)'), 0);
console.log('D');
// Order: A, D, B, C

Knowing this helps debug “why did my handler run first?” moments.


When not to use a Promise alone

A JavaScript Promise doesn’t make CPU-heavy work faster—it just schedules results. For big computations, move work to a Web Worker (or a server) and optionally return a Promise that resolves when the worker posts back. Promises orchestrate; workers do the heavy lifting.


Quick combinator cheat sheet

  • Promise.all — fail fast if any rejects; great for loading independent data together.
  • Promise.allSettled — never throws; inspect each result’s {status, value|reason}.
  • Promise.race — first to settle wins (fulfill or reject).
  • Promise.any — first to fulfill wins; rejects only if all reject.

Debugging tips

  • Always end chains with a .catch() during development—surface errors early.
  • In async functions, log the stack: console.error(err.stack) gives better traces.
  • Name your functions (async function getUser() {}) so stack traces are readable.
  • Prefer one style per block (either chain or async/await) to avoid confusion.

Key takeaways

  • JavaScript Promises model a future value.
  • Use then/catch/finally for quick chains; prefer async/await for readability.
  • Reach for Promise.all when tasks can run in parallel.

Internal reading:

  • Understanding JavaScript Scope: A Beginner’s Guide
  • Emmet Tips & Tricks for Beginners

External references:

  • MDN: Promise · Async/Await · Promise.all

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

← Previous Post Emmet Tips & Tricks for Beginners (That You’ll Actually Use)
Next Post → 10 Must-Use HTML Tags in 2025

Categories

  • Accessibility
  • Case Studies
  • CSS
  • Design
  • Front-End
  • HTML
  • JavaScript
  • News
  • Productivity
  • Random Thoughts
  • SEO
  • Themes
  • Trends
  • Tutorials
  • TypeScript
  • TypeSCript From The Ground Up
  • UX Engineering
  • Web Development
  • Wordpress

Recent Posts

  • Native CSS Is Quietly Replacing Sass, But It Isn’t Replacing the “Need” for Sass
  • Everyday Types Explained (From the Ground Up)
  • 2026 CSS Features You Must Know (Shipped Late 2025–Now)
  • 60 JavaScript Projects in 60 Days
  • JavaScript vs TypeScript: What Actually Changes

Tags

accessibility accessible web design ADA compliance async components Career Journey cascade layers code splitting composables composition api computed properties container queries css Design Inspiration Design Systems disability access File Organization Front-End Development Frontend frontend development immutability javascript JavaScript reducers lazy loading Material Design Modern CSS performance Personal Growth react React useReducer Redux Resume screen readers seo Suspense Teleport TypeScript UI/UX UI Engineering UX UX Engineering Vue Router WCAG web accessibility Web Development Web Performance

Riad Kilani Front-End Developer

© 2026 Riad Kilani. All rights reserved.