Asynchronous code is at the heart of JavaScript. Understanding it unlocks the full power of the language.

The problem: callbacks

The old way to handle async work was callbacks:

javascript
fetchUser(id, function(err, user) {
  if (err) return handleError(err);
  fetchPosts(user.id, function(err, posts) {
    if (err) return handleError(err);
    render(posts);
  });
});

This leads to “callback hell” — deeply nested, hard-to-read code.

Promises to the rescue

A Promise represents a value that may be available now, in the future, or never:

javascript
fetchUser(id)
  .then(user => fetchPosts(user.id))
  .then(posts => render(posts))
  .catch(err => handleError(err));

Much cleaner! The .then() chain reads left-to-right.

Creating your own Promise

javascript
function delay(ms) {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
}

delay(1000).then(() => console.log('1 second passed'));

async/await: synchronous-looking async code

async/await is syntax sugar over Promises:

javascript
async function loadDashboard(userId) {
  try {
    const user  = await fetchUser(userId);
    const posts = await fetchPosts(user.id);
    render(posts);
  } catch (err) {
    handleError(err);
  }
}

An async function always returns a Promise. Inside it, await pauses execution until the Promise resolves.

Running tasks in parallel

Use Promise.all when tasks are independent:

javascript
async function loadAll(userId) {
  const [user, settings] = await Promise.all([
    fetchUser(userId),
    fetchSettings(userId)
  ]);
  return { user, settings };
}

Promise.all rejects as soon as any Promise rejects. Use Promise.allSettled to wait for all, regardless of outcome.

Error handling patterns

javascript
// Option 1: try/catch
async function safe() {
  try {
    return await riskyOperation();
  } catch (err) {
    console.error('Failed:', err);
    return null;
  }
}

// Option 2: catch on the awaited promise
const result = await riskyOperation().catch(() => null);

Summary

API When to use
Promise Wrapping async APIs, composing chains
.then().catch() Functional style, libraries
async/await Application code, readability
Promise.all Independent parallel tasks
Promise.allSettled When you need all results regardless of failure