6.8 — Parallel & racing
Lesson 6.6 left a warning: await a(); await b(); runs one after the other — two independent 80ms waits become 160ms. In a test suite fetching five fixtures per test, that's the difference between a 2-minute run and a 10-minute one. Independent waits should overlap.
JavaScript ships four combinators for exactly this — Promise.all, allSettled, race, any — each answering a different question about a set of promises: all of them? every verdict? the first to settle? the first to succeed?
Plus one ordering surprise that trips even seniors: results come back in input order, not finish order.
function delay(ms, value) {
return new Promise((resolve) => {
setTimeout(() => resolve(value), ms);
});
}
async function main() {
const salad = delay(60, "salad");
const soup = delay(20, "soup");
const first = await Promise.race([salad, soup]);
console.log(first);
const both = await Promise.all([salad, soup]);
console.log(both);
}
main();The bill, first: await delay(60) then await delay(20) = 80ms total, because the second timer doesn’t even START until the first await finishes.
The deeper story, with the real names for things — this part is what turns “I saw it” into “I can explain it.”
None of these run promises "in parallel" themselves — the overlap happened when you started the work (the executor runs at construction, 6.4). The combinators just observe a set of receipts and settle their own single receipt by a rule. One thread throughout (6.1's law): the waiting overlaps in the environment, never the JS.
💼 On the job — Playwright code awaits sequentially when steps depend (click → then assert). Promise.all is for genuinely simultaneous things. The classic: await Promise.all([page.waitForNavigation(), page.click("a")]) — start listening BEFORE the click that triggers it. Starting-then-awaiting is a professional reflex; today it enters your hands.
And the input-order guarantee is what makes all composable with destructuring: const [user, cart] = await Promise.all([getUser(), getCart()]) — 4.11's array pattern, matched by position, safe because position is promised.
⌨️ race them, then feed everyone
Two dishes, different cooking times. Prove you can ask both of the phase’s parallel questions: who’s FIRST — and, when everything’s ready, in what ORDER do the results arrive?
requirements:
- A helper
delay(ms, value)— a promise resolving withvalueafterms. - Start BOTH before any await:
slow= 60ms →"salad",fast= 20ms →"soup". Await the RACE of [slow, fast] and print the winner. - Then await ALL of [slow, fast] and print the array — and notice it comes back in INPUT order, salad first, even though soup finished first.
when you press RUN, the console must show exactly:
✏️ Quick check 1
a takes 300ms, b takes 500ms, both started first. How many ms until await Promise.all([a, b]) finishes? Type the number.
✏️ Quick check 2
p1 (80ms → "A") and p2 (30ms → "B"). Type EXACTLY what await Promise.all([p1, p2]) fulfills with:
✏️ Quick check 3
Five promises; two reject. Which combinator fulfills anyway, with a verdict for each? Type its name.
🗣️ Now teach it back
Explain to a friend: why do two independent awaits waste time, how do you overlap them, what do all / allSettled / race / any each answer — and what order do all’s results arrive in?
Write it as if your friend is sitting next to you. Saved to your journal — future-you will use these notes to teach others.