Module 8: Asynchronous JavaScript
The Event Loop, Tasks, and Microtasks
Explain why synchronous code, promise handlers, and timer callbacks run in different order.
JavaScript execution in a browser typically runs a task to completion on the main thread before the runtime processes the next task. The event loop coordinates tasks, rendering opportunities, and microtasks. Exact host scheduling details vary, but a useful beginner model is: run current synchronous code, then drain microtasks, then process another task when the host is ready.
console.log("A");
setTimeout(() => console.log("B: timer"), 0);
Promise.resolve().then(() => console.log("C: promise"));
console.log("D");
A typical order is A, D, C: promise, then B: timer. Both the timer and promise callbacks run later, but promise reactions use the microtask queue, which is processed before the next timer task in this example.
Why this matters#
A long synchronous loop can make a page unresponsive because rendering and user events cannot be handled until the current work yields. For expensive work, consider splitting it into smaller chunks or using Web Workers for supported CPU-heavy tasks.
Async is not necessarily parallel#
async/await makes promise-based code easier to express; it does not by itself move CPU-intensive JavaScript onto another thread. Some I/O is handled by the host runtime, and workers can provide parallel execution for suitable tasks.
Avoid fragile assumptions#
Do not use arbitrary zero-delay timers as a general synchronization strategy. Prefer awaiting the promise that represents the work you depend on.
Practice#
Predict the console order for a snippet containing synchronous logs, a resolved promise, and two timers. Run it and explain the difference between tasks and microtasks.