Modern JavaScript Guide

Master Asynchronous
JavaScript

From callbacks to Promises to Async/Await. Learn how to write clean, non-blocking code with interactive examples and visual explanations.

Understanding Promise States

A Promise is an object representing the eventual completion or failure of an asynchronous operation. It exists in one of three states:

Pending

Initial state, neither fulfilled nor rejected. The operation is still ongoing.

Fulfilled

Operation completed successfully. The promise has a resolved value.

Rejected

Operation failed. The promise has a reason for the failure.

promise-basics.js
const myPromise = new Promise((resolve, reject) => {
  const success = true;
  
  setTimeout(() => {
    if (success) {
      resolve("Operation successful! 🎉");
    } else {
      reject("Operation failed! ❌");
    }
  }, 2000);
});

// Consuming the Promise
myPromise
  .then(result => {
    console.log("Success:", result);
  })
  .catch(error => {
    console.log("Error:", error);
  })
  .finally(() => {
    console.log("Cleanup complete");
  });

Promise Chaining

Avoid "Callback Hell" by chaining promises. Each .then() returns a new promise, allowing sequential asynchronous operations.

Execution Flow

1

fetchUserData(123)

Returns Promise<User>

2

.then(user => ...)

Returns Promise<Posts>

3

.then(posts => ...)

Returns postCount

chaining.js
function fetchUserData(userId) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ id: userId, name: "John Doe" });
    }, 1000);
  });
}

function fetchUserPosts(user) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([
        { title: "Post 1", content: "Hello" },
        { title: "Post 2", content: "World" }
      ]);
    }, 1000);
  });
}

// Chaining
fetchUserData(123)
  .then(user => {
    console.log("User:", user);
    return fetchUserPosts(user);
  })
  .then(posts => {
    console.log("Posts:", posts);
    return posts.length;
  })
  .then(count => {
    console.log("Total:", count);
  })
  .catch(error => {
    console.error("Error:", error);
  });
Syntactic Sugar

Async/Await

Write asynchronous code that looks and behaves like synchronous code. Easier to read, easier to debug.

async

The async Keyword

Automatically wraps the return value in a Promise. Makes any function return a Promise.

await

The await Keyword

Pauses execution until the Promise resolves. Can only be used inside async functions.

Error Handling

Use standard try/catch blocks instead of .catch(). Much more intuitive!

promise-style.js
function getData() {
  return fetchUserData(123)
    .then(user => {
      console.log(user);
      return fetchUserPosts(user);
    })
    .then(posts => {
      console.log(posts);
      return processPosts(posts);
    })
    .then(result => {
      console.log(result);
    })
    .catch(error => {
      console.error(error);
    });
}

Key Insight: Both examples do exactly the same thing! Async/await is just cleaner syntax for the same Promise-based behavior.

Promise Static Methods

Utility methods for handling multiple promises concurrently. Essential for performance optimization.

Promise.all()

Wait for all or fail fast

Waits for all promises to resolve. If any promise rejects, immediately rejects with that error.

Promise.all([
  p1, p2, p3
]).then(values => {
  // [v1, v2, v3]
});
Resolves when slowest promise resolves

Promise.race()

First to settle wins

Returns the first promise that settles (resolves or rejects). Great for timeouts!

Promise.race([
  fetch(url),
  timeout(5000)
]).then(winner => {
  // First done
});
Resolves/rejects with first result

Promise.allSettled()

Wait for all regardless

Waits for all promises to settle, regardless of outcome. Never rejects, always resolves with status array.

Promise.allSettled([
  p1, p2, p3
]).then(results => {
  // [{status, value}, ...]
});
Always resolves, inspect status

Interactive Playground

Run live JavaScript demos to see Promises in action

Demo Controls

Console Output
// Click a demo button above to see output...

Promises vs Async/Await

Aspect Promises (.then/.catch) Async/Await
Syntax Style Functional, chain-based Procedural, synchronous-like
Error Handling .catch() try/catch blocks
Readability Can become nested (callback hell) Flat, linear structure
Debugging Harder to step through Standard debugging (breakpoints)
Browser Support ES6 (2015)+ ES2017+ (Modern browsers)

Best Practices

1

Always Handle Errors

Use .catch() or try/catch for every async operation to prevent unhandled promise rejections.

2

Prefer Async/Await

Use async/await for cleaner code, but understand Promises underneath.

3

Run in Parallel

Use Promise.all() for independent operations instead of awaiting sequentially.

4

Don't Mix Styles

Avoid mixing .then() with async/await in the same function for consistency.