Event Loop & Asynchronous JS
Event Loop & Asynchronous JS: Understanding JavaScript's Event Loop is essential for fixing timing issues in Playwright tests.
Understanding JavaScript's Event Loop is essential for fixing timing issues in Playwright tests. JavaScript is single-threaded: only one operation can happen at a time — like queuing up at a store with a single cashier. But how does this single cashier manage long operations like network requests or timers without blocking? It delegates them to Web APIs: `fetch()`, `setTimeout()` and similar operations are handed off to browser background services, and when they complete they are placed in the Callback Queue. The Event Loop picks up the next queued task the moment the Call Stack is empty. Why is this different from Java? Java is multi-threaded: a new thread is opened for a network request. JavaScript manages asynchronous work sequentially on a single thread — `async/await` makes this sequence elegant and readable. Playwright must use `await` because all browser actions return asynchronous Promises.
How the Event Loop Works
🎬 A, D, C, B: Behind the Scenes of Event Loop Ordering
Microtask Queue (Promise)
Macrotask Queue (setTimeout)
Code runs: `console.log("A")`, `setTimeout(()=>console.log("B"),0)`, `Promise.resolve().then(()=>console.log("C"))`, `console.log("D")`. What order prints?
Step 1 — the Call Stack runs synchronous code IN ORDER: `"A"` prints first. `setTimeout` is DELEGATED to the Web API and does not block the stack.
Step 2 — `Promise.resolve().then(...)` runs: its callback goes straight into the Microtask Queue (does not block the Call Stack). Then `console.log("D")` runs synchronously and `"D"` prints.
Step 3 — the Call Stack is now COMPLETELY empty: synchronous code (`A`, `D`) is done. By Event Loop rule, the Microtask Queue is drained FIRST — `"C"` prints.
Step 4 — only AFTER the Microtask Queue is fully drained does the Event Loop check the Macrotask Queue (`setTimeout` callback) and push it into the Call Stack — `"B"` prints last.
Final — the order is definitively `A → D → C → B`. In Playwright, forgetting `await` makes test code assume the wrong order and run an assertion before the element is actually ready — the classic flaky test source.
Step by Step: The Event Loop Mechanism
Call Stack runs sync code
Synchronous lines like console.log run in order on the Call Stack.