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.
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.
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.
⌨️ 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 afterms(setTimeout inside). - An async function
fetchScore(): await a 40ms delay, then return42. And an async functionfetchBroken(): await a 40ms delay, thenthrow new Error("offline"). - An async
main(): in atry, awaitfetchScore()and print"score: " + it; then awaitfetchBroken(). In thecatch, print the error's message. Callmain().
when you press RUN, the console must show exactly:
✏️ 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.
🗣️ 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.