You can now read values, shapes, and the verbs that work on them; today is the final piece, the one that makes tests honest about time. A browser is slow compared to your code. It fetches, renders, and animates while your test sprints ahead, and JavaScript handles that gap with promises. A promise is a receipt: the work is happening, the result arrives later. The word await means stand here until the receipt pays out.
The store’s slow feed, in miniature
The Tumble Kitchen product feed takes about 700 milliseconds on purpose. Here is that delay reproduced in eight lines. Save it as feed.js and run node feed.js:
const pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function fetchPrice() {
await pause(700);
return 4500;
}
async function main() {
const price = await fetchPrice();
console.log(price.toLocaleString('en-JM') + ' JMD');
}
main();
Read it back: fetchPrice takes 700 milliseconds to answer, the await inside main holds the line until the price actually exists, and you get 4,500 JMD. Now delete the word await from the price line and run it again. It prints [object Promise] JMD: a receipt where the money should be. The word async on a function just marks it as one that hands out receipts and is allowed to use await inside.
Why every Playwright line awaits
Every Playwright action is a message to a real browser over a connection: go here, type this, click that. Every one of them returns a promise, which is why your test function is marked async and why every line that touches the page starts with await. The assertions await too, because expect checks the live page and retries until it agrees or times out.
Here is what one forgotten await does. This is a real test; do not fix it yet:
import { test, expect } from '@playwright/test';
test('search shows jerk dishes', async ({ page }) => {
await page.goto('https://juliapottinger.com/practice/tumble-kitchen/');
// The await is missing on the next line. Do not copy this.
page.getByTestId('search-input').fill('jerk');
await page.getByTestId('search-submit').click();
await expect(page.getByTestId('product-card').first()).toContainText('Jerk');
});
Read it back: fill returns a promise that nobody waits for, so the click races the typing. Some runs the word lands first and the test passes. Some runs the click wins, the form submits an empty search, the full twelve-dish menu comes back, and the first card is the Deluxe Plantain Plate, so the assertion fails. Same code, two answers, and nothing in the product changed between them. That is the exact shape of the flake that kills suites, and it costs one word to cause.
The fix is the word:
await page.getByTestId('search-input').fill('jerk');
If a line touches the page, it starts with await. No exceptions, and no judgment calls to get wrong at review time.
Do this today
Run feed.js, remove the await, and watch the receipt print. Then type the broken test into your project and run it several times in one go with npx playwright test —repeat-each 5. Watch it flip between pass and fail, then add the missing word and watch five straight greens. That is the whole JavaScript toolkit this course needs. Tomorrow you write the first test of your real suite and read every line of it aloud.
