JS Sketchbooksee JavaScript think ✏️
← back to phase 6

6.6 — async/await

Promise chains flattened the pyramid — but they still read like plumbing: .then, arrow, return, .then. JavaScript's final gift to async is a costume that makes promise code look synchronous: async/await.

Two keywords, two precise meanings. async before a function: "this function always returns a promise."

await before a promise: "pause this function here — park its frame on a shelf, free the stack — and resume with the value when the promise settles." Not blocking. Pausing one function while the world keeps moving.

This is the syntax your entire Playwright career will be written in — today it stops being magic.

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

async function brew() {
  console.log("kettle on");
  await delay(60);
  console.log("tea ready");
  return "cup";
}

console.log("before");
brew().then((c) => console.log("got " + c));
console.log("after");

"before" prints, then brew() is called — an async function starts running IMMEDIATELY and synchronously, like any function: "kettle on" prints. The async keyword changed nothing yet. The change comes at the first await.

call stackglobalbrew()the paused shelf(empty)await parks a frame here —resumes via the micro lane ⚡an async function is called like any function — it starts runningimmediately, on the stack, until its first awaitbefore · kettle on
under the hood

The deeper story, with the real names for things — this part is what turns “I saw it” into “I can explain it.”

Await accepts any promise — which is why it composes with everything: await fetch(…) (next lesson), await page.click(…) (Phase 11), await Promise.all([…]) (6.8). Awaiting a non-promise value just continues with it (wrapped in an instantly-fulfilled promise).

And await is only legal inside async functions (plus at the top level of modules) — the engine needs permission to suspend the frame.

The subtle cost that bites real test suites: await a(); await b(); is sequential — b doesn't start until a finishes. Two independent 80ms waits become 160ms. When steps don't depend on each other, start both promises first, then await — lesson 6.8 turns this into a tool (Promise.all).

Mental model to keep forever: async/await changes how async code READS, never how it RUNS. Underneath: the same receipts (6.4), the same microtask lane (6.5), the same event loop (6.2). You can now read all four layers of the machine — that's the phase checkpoint's whole job, two lessons from now.

your turn

⌨️ await, and catch what falls

Write async code that reads like sync code — including the part where 5.8’s try/catch finally works on async failures again.

requirements:

  • A helper delay(ms) returning a promise that resolves after ms (setTimeout inside).
  • An async function fetchScore(): await a 40ms delay, then return 42. And an async function fetchBroken(): await a 40ms delay, then throw new Error("offline").
  • An async main(): in a try, await fetchScore() and print "score: " + it; then await fetchBroken(). In the catch, print the error's message. Call main().

when you press RUN, the console must show exactly:

score: 42
offline

✏️ Quick check 1

Type the FULL output order, separated by spaces:

async function go() {
  console.log("in");
  await Promise.resolve();
  console.log("resumed");
}
console.log("start");
go();
console.log("end");

✏️ Quick check 2

What does EVERY async function return? Type the one word.

✏️ Quick check 3

await pauses ___ — type: the whole thread, or just this function.

teach it back

🗣️ Now teach it back

Explain async/await to a friend who knows promises: what async guarantees, what await actually does to the function (and the thread!), how it resumes, and why try/catch works again.

Write it as if your friend is sitting next to you. Saved to your journal — future-you will use these notes to teach others.

a few sentences, minimum — you’ve got this
to remember
async fn ⇒ ALWAYS returns a promise (return → fulfill, throw → reject). Runs synchronously until its first await.
await = park THIS frame (bookmarked) + free the stack; resume via the microtask lane with the settled value. Suspension, never blocking.
try/catch works across await (rejections re-throw at the await line). Beware sequential awaits for independent work — 6.8 fixes that.
next: 6.7 fetch & APIs →