0Pricing
Frontend Academy · Lesson

async/await and Error Handling

Write async functions with the await keyword, wrap awaited calls in try/catch blocks, and understand how async/await is syntactic sugar over Promises.

async/await and Error Handling is a free Frontend Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Frontend Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What Is async/await?

async/await is syntactic sugar over Promises. An async function always returns a Promise. The await keyword pauses the function until the awaited Promise settles. Code looks synchronous but runs asynchronously.

async Function

Add the async keyword before a function declaration or expression. The function now implicitly wraps its return value in a Promise.

async function fetchUser(id) {
  // implicitly returns a Promise
  return { id, name: 'Alice' };
}

// Equivalent to:
function fetchUser(id) {
  return Promise.resolve({ id, name: 'Alice' });
}

await — Pausing for Promises

await can only be used inside an async function. It pauses execution until the Promise resolves and returns the resolved value. Other code in the event loop continues running.

async function loadProfile(id) {
  const user = await fetchUser(id);     // waits for Promise
  const posts = await fetchPosts(user.id); // waits again
  return { user, posts };
}

Sequential vs Parallel Awaits

Awaiting sequentially is easy to read but slow if the tasks don't depend on each other. Use Promise.all() to await multiple independent tasks in parallel.

// Sequential (slow if tasks are independent):
const user = await fetchUser(id);
const settings = await fetchSettings(id); // waits for user unnecessarily

// Parallel (fast):
const [user, settings] = await Promise.all([
  fetchUser(id),
  fetchSettings(id),
]);

try/catch for Error Handling

Wrap awaited calls in try/catch to handle rejections. The catch block receives the rejection reason as an error object.

async function saveUser(data) {
  try {
    const response = await fetch('/api/users', {
      method: 'POST',
      body: JSON.stringify(data),
      headers: { 'Content-Type': 'application/json' }
    });
    if (!response.ok) throw new Error(`Server error ${response.status}`);
    return await response.json();
  } catch (err) {
    console.error('Save failed:', err.message);
    throw err; // re-throw so caller can handle
  }
}

Top-Level await

In ES modules (not CommonJS), you can use await at the top level without wrapping in an async function. Useful for module initialisation.

// module.js (ES module)
const config = await fetch('/config.json').then(r => r.json());
export const API_URL = config.apiUrl;

async/await with forEach — Pitfall

Array.forEach() doesn't await async callbacks. Use for...of for sequential async iteration, or Promise.all(arr.map(...)) for parallel.

// WRONG — forEach doesn't await:
items.forEach(async (item) => {
  await processItem(item); // runs but forEach doesn't wait
});

// CORRECT — sequential:
for (const item of items) {
  await processItem(item);
}

// CORRECT — parallel:
await Promise.all(items.map(item => processItem(item)));

Error Objects and Custom Errors

Always throw Error objects (not strings) so stack traces are preserved. Create custom error classes for domain-specific errors that callers can catch by type.

class NetworkError extends Error {
  constructor(message, status) {
    super(message);
    this.name = 'NetworkError';
    this.status = status;
  }
}

try {
  const res = await fetch(url);
  if (!res.ok) throw new NetworkError(`Failed`, res.status);
} catch (err) {
  if (err instanceof NetworkError && err.status === 401) {
    redirectToLogin();
  } else {
    throw err;
  }
}

finally in async/await

The finally block runs whether the try succeeded or failed — just like .finally() on a Promise. Use it to release resources or hide loading states.

async function loadData() {
  setLoading(true);
  try {
    const data = await fetch('/api/data').then(r => r.json());
    setData(data);
  } catch (err) {
    setError(err.message);
  } finally {
    setLoading(false); // always runs
  }
}

Async IIFE for Module Entry Points

Wrap the entry point of a script in an async IIFE when you can't use top-level await.

(async () => {
  const data = await fetchInitialData();
  renderApp(data);
})();

AbortController for Cancellation

Pass an AbortController signal to fetch to cancel an in-flight request. Catching the AbortError separately prevents showing an error to the user.

const controller = new AbortController();

try {
  const data = await fetch(url, { signal: controller.signal }).then(r => r.json());
  setData(data);
} catch (err) {
  if (err.name !== 'AbortError') setError(err.message);
}

// Cancel:
controller.abort();

Quick Check

Which construct correctly handles errors from an awaited Promise?

Recap: async/await

async functions always return Promises. await pauses until a Promise settles. Use try/catch for error handling. For parallel operations use Promise.all(). Avoid await inside forEach — use for...of or Promise.all. AbortController cancels in-flight requests. Top-level await works in ES modules.

Frequently asked questions

Is the “async/await and Error Handling” lesson free?

Yes — the full text of “async/await and Error Handling” is free to read here on the web, and the Frontend Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Frontend Academy course, upgrade to CoddyKit PRO.

What will I learn in “async/await and Error Handling”?

Write async functions with the await keyword, wrap awaited calls in try/catch blocks, and understand how async/await is syntactic sugar over Promises. You practise Frontend Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Frontend Academy?

No prior experience is required. Frontend Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “async/await and Error Handling” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Frontend Academy lesson?

Yes. Every Frontend Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. The Event Loop: Call Stack Queue Microtasks
  2. Callbacks and Callback Hell
  3. Promises: then catch finally Promise.all
  4. async/await and Error Handling
← Back to Frontend Academy