Async JavaScript — Promises & Async/Await

Understand how JavaScript handles waiting — the key concept behind every Playwright test.

Playwright Automation Prep Lesson 7
14 min read

What you'll learn

  • Understand why async code exists (waiting for things)
  • Read and write async/await functions
  • Handle errors with try/catch
  • Understand why every Playwright action needs await

Async JavaScript --- Promises & Async/Await

This is the single most important concept for Playwright testing. Every Playwright command uses await. Nail this lesson and Playwright will make perfect sense.

Why Async Exists

Slow things that need waiting: loading webpages, calling APIs, reading files, clicking buttons in a browser test. If JavaScript froze each time, everything would lock up.

Order food

STAND HERE

Blocked!

Get food

Synchronous = blocking. You wait in line doing nothing.

Order food

Sit + do stuff

Food arrives

Asynchronous = not blocking. You can do other things while waiting.

Synchronous vs Asynchronous

Synchronous

  • One line at a time, top to bottom
  • Each line waits for the previous
  • Simple, predictable order

Asynchronous

  • Start a slow task, keep going
  • Come back when it finishes
  • Order may surprise you at first
// Async surprise:
console.log("1");
setTimeout(() => console.log("2"), 1000);
console.log("3");

// Output:
// 1
// 3    ← 3 runs before 2!
// 2    ← 2 arrives one second later

What is a Promise?

Three states: pending (still brewing), fulfilled (here’s your coffee!), rejected (we’re out of oat milk).

You rarely create Promises yourself. Functions like fetch() already return them. Your job is to handle them with await.

Async / Await

The await flow

call async function

returns Promise

a receipt

await pauses

value arrives

continue

How await pauses execution

  1. 1

    Start the async function

    async function getUser() {
  2. 2

    Hit await — function pauses here

      const response = await fetch(url);
  3. 3

    Fetch finishes — resume with real value

      // response is now actual data, not a Promise
  4. 4

    Another await — pause again

      const user = await response.json();
  5. 5

    All done — use the data

      console.log(user.name);
    }
async function getUser() {
  const response = await fetch("https://api.example.com/user");
  const user = await response.json();
  console.log(user.name);
}

Quick check

Question 1 of 10 correct

Where can you use the 'await' keyword?

Error Handling with try/catch

No error handling

  • A failed fetch crashes your program
  • No way to recover gracefully
  • User sees a cryptic error

With try / catch

  • Catches the error cleanly
  • Show a friendly message, retry, etc.
  • Program keeps running
async function fetchUser() {
  try {
    const response = await fetch("https://api.example.com/user");
    const user = await response.json();
    console.log("User:", user.name);
  } catch (error) {
    console.log("Oops, something failed:", error.message);
  }
}

Why This Matters for Playwright

// Real Playwright test — notice async + await everywhere
test("user can log in", async ({ page }) => {
  await page.goto("https://myapp.com/login");
  await page.fill("#email", "alice@example.com");
  await page.fill("#password", "secret123");
  await page.click("#login-button");
  await expect(page).toHaveURL("/dashboard");
});

Practice Exercises

Complete the Async Function

Add the missing async and await keywords to make this function work correctly.

 function loadUserData() {
const response =  fetch("https://api.example.com/users");
const users =  response.json();
console.log("Loaded", users.length, "users");
}

Fill in the Blank

To pause execution until a promise resolves, use the keyword.

Mini Challenge: try / catch

To handle errors from an await that might fail, wrap it in a try / block.

Check Your Understanding

Question 1 of 30 correct

What happens if you write `const response = fetch(url);` WITHOUT the await keyword?