JS

JavaScript Fundamentals

25 lessons

Progress0%
1. Introduction to JavaScript
1What is JavaScript?2Setting Up Your Environment
2. Variables and Data Types
1Declaring Variables2Data Types3Type Conversion
3. Operators
Arithmetic OperatorsComparison OperatorsLogical Operators
4. Control Flow
Conditional StatementsLoops
5. Functions
Function Basics
6. Arrays & Iteration
Array MethodsSpread, Rest & Destructuring
7. Objects & JSON
Working with ObjectsJSON & Optional Chaining
8. OOP & Classes
Class BasicsInheritance & Private Fields
9. Modules & Modern JS
ES ModulesModern JavaScript Features
10. Async JavaScript
PromisesAsync/Await
11. Error Handling
Error Types & try/catchCustom Errors & Debugging
12. Iterators & Advanced
Iterators & GeneratorsMap, Set & WeakRefs
All Tutorials
JavaScriptAsync JavaScript
Lesson 21 of 25 min
Chapter 10 · Lesson 2

Async/Await

Async/Await

Async/await is syntax built on top of Promises that lets you write asynchronous code that reads like synchronous code. It was standardized in ES2017 and has become the dominant way to handle async operations.

async Functions

The async keyword before a function declaration makes it return a Promise automatically:

js
async function getUser() {
  return { name: "Alice" }; // wrapped in Promise.resolve() automatically
}
getUser().then(console.log); // { name: "Alice" }

await

Inside an async function, await pauses execution until the Promise resolves and unwraps its value:

js
async function main() {
  const user = await fetchUser(1);   // waits for the Promise
  console.log(user.name);
}

await can only be used inside an async function (or at the top level of an ES module).

Error Handling with try/catch

Use standard try/catch blocks — they catch Promise rejections transparently:

js
async function safe() {
  try {
    const data = await riskyOperation();
    return data;
  } catch (err) {
    console.error(err.message);
  } finally {
    cleanup();
  }
}

Running in Parallel

A common mistake is awaiting sequentially when the operations are independent:

js
// SLOW — sequential (waits for each before starting next)
const a = await fetchA();
const b = await fetchB();

// FAST — parallel (both start at the same time)
const [a, b] = await Promise.all([fetchA(), fetchB()]);

The Fetch API

The Fetch API is the modern browser standard for HTTP requests, returning Promises:

js
async function getPost(id) {
  const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  return response.json(); // also a Promise
}

Code Examples

Basic async/await with try/catchjavascript
// Simulated async data fetcher
function getUser(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id === 1) resolve({ id: 1, name: "Alice", deptId: 10 });
      else          reject(new Error(`User ${id} not found`));
    }, 50);
  });
}

function getDepartment(deptId) {
  return new Promise((resolve) => {
    setTimeout(() => resolve({ id: deptId, name: "Engineering" }), 50);
  });
}

async function loadUserProfile(userId) {
  try {
    const user = await getUser(userId);
    const dept = await getDepartment(user.deptId);
    console.log(`${user.name} works in ${dept.name}`);
  } catch (err) {
    console.error("Failed:", err.message);
  } finally {
    console.log("Load attempt finished");
  }
}

loadUserProfile(1);
loadUserProfile(99);

async/await makes sequential async operations read like synchronous code. The try/catch block catches any rejection in the await chain, just like a synchronous exception.

Sequential vs Parallel Executionjavascript
const delay = (ms, val) => new Promise((res) => setTimeout(() => res(val), ms));

async function sequential() {
  console.time("sequential");
  const a = await delay(100, "A");
  const b = await delay(100, "B");
  const c = await delay(100, "C");
  console.timeEnd("sequential");
  return [a, b, c];
}

async function parallel() {
  console.time("parallel");
  const [a, b, c] = await Promise.all([
    delay(100, "A"),
    delay(100, "B"),
    delay(100, "C"),
  ]);
  console.timeEnd("parallel");
  return [a, b, c];
}

async function main() {
  const s = await sequential();
  console.log("Sequential:", s);
  const p = await parallel();
  console.log("Parallel:  ", p);
}

main();

Sequential awaits take the sum of all delays. Parallel with Promise.all takes only the maximum delay. When independent async tasks don't depend on each other's results, always run them in parallel.

Async Error Handling Patternsjavascript
// Helper: wrap a promise to return [error, data]
async function to(promise) {
  try {
    const data = await promise;
    return [null, data];
  } catch (err) {
    return [err, null];
  }
}

const goodFetch = () => Promise.resolve({ status: 200, data: "payload" });
const badFetch  = () => Promise.reject(new Error("Network timeout"));

async function main() {
  // Pattern 1: traditional try/catch
  try {
    const result = await goodFetch();
    console.log("Good fetch:", result.data);
  } catch (e) {
    console.error(e.message);
  }

  // Pattern 2: Go-style [error, data] tuple
  const [err1, data1] = await to(goodFetch());
  const [err2, data2] = await to(badFetch());

  if (!err1) console.log("Pattern 2 success:", data1.data);
  if (err2)  console.log("Pattern 2 error:", err2.message);
}

main();

The Go-style [error, data] wrapper eliminates nested try/catch blocks when handling multiple async calls. Both patterns are valid — choose based on readability for your use case.

Quick Quiz

1. What does an async function always return?

2. What is wrong with: const a = await fetchA(); const b = await fetchB(); when fetchA and fetchB are independent?

3. Where can the await keyword be used?

Was this lesson helpful?

PreviousNext