page.click("#submit"), Node sends a WebSocket message to the browser, the browser scrolls, clicks, and sends a response back. Therefore, every Playwright operation returns Promise<void> or Promise<T>.---
await// ❌ WRONG: page.click returns a Promise. Without await, Node fires and moves to the next line immediately!
page.click("button#login");
page.fill("input#code", "1234"); // Fails because page hasn't navigated or button click didn't finish!// ✅ CORRECT:
await page.click("button#login");
await page.fill("input#code", "1234");
---
// Sequential: Task B relies on Task A
async function loginWorkflow() {
await fillUsername();
await fillPassword();
await clickSubmit();
}// Parallel: Tasks are completely independent (e.g., preparing two API preconditions)
async function prepareTestData() {
const [userResponse, inventoryResponse] = await Promise.all([
fetch("https://api.test/user"),
fetch("https://api.test/inventory"),
]);
return { userResponse, inventoryResponse };
}
---
export async function retryOperation<T>(
action: () => Promise<T>,
retries: number = 3,
delayMs: number = 1000
): Promise<T> {
let lastError: unknown;
for (let attempt = 1; attempt <= retries; attempt++) {
try {
return await action();
} catch (err) {
lastError = err;
if (attempt < retries) {
await new Promise((resolve) => setTimeout(resolve, delayMs));
}
}
}
throw lastError;
}