Event Loop Mechanism in JavaScript
Event Loop Mechanism in JavaScript
Description
The event loop is the core mechanism that enables asynchronous programming in JavaScript. As a single-threaded language, JavaScript uses the event loop model to handle asynchronous operations (such as timers and network requests) without blocking the main thread. Understanding the event loop requires grasping concepts like the call stack, task queue, and microtask queue.
Problem-Solving Process
-
Basic Concepts
- Call Stack: Tracks the currently executing function. When a function is called, it is pushed to the top of the stack; after execution completes, it is popped off the stack.
- Web APIs: Asynchronous features provided by the browser (e.g.,
setTimeout,fetch). JavaScript delegates these operations to the browser's background processing, and once completed, the callback functions are placed into the task queue. - Task Queue (Macrotask Queue): Holds callback functions from asynchronous operations (e.g., callbacks from
setTimeout, DOM events). - Microtask Queue: Holds callbacks that need to be executed as soon as possible (e.g.,
Promise.then,MutationObserver).
-
Workflow of the Event Loop
- Step 1: Execute synchronous code in the call stack (highest priority). When asynchronous operations are encountered, delegate them to Web APIs for processing.
- Step 2: After synchronous code execution completes, the event loop first checks the microtask queue, executing all microtasks in sequence (until the queue is empty).
- Step 3: After microtasks are executed, render the page if necessary (timing determined by the browser).
- Step 4: Retrieve one macrotask from the task queue and execute it (e.g., a
setTimeoutcallback), then return to Step 2 to check the microtask queue.
-
Example Analysis
console.log("Start"); setTimeout(() => console.log("Timeout"), 0); Promise.resolve().then(() => console.log("Promise")); console.log("End");- Execution Process:
- Synchronous code: Output "Start" → Delegate
setTimeoutcallback to Web APIs (placed into the task queue after 0 ms) → PlacePromise.thencallback into the microtask queue → Output "End". - After synchronous code ends, check the microtask queue: Output "Promise".
- After the microtask queue is cleared, retrieve the
setTimeoutcallback from the task queue and execute it: Output "Timeout".
- Synchronous code: Output "Start" → Delegate
- Execution Process:
-
Key Rules
- Microtasks have higher priority than macrotasks (e.g.,
Promise.thenexecutes beforesetTimeout). - Tasks of the same type are executed in order, but the microtask queue must be completely cleared before processing macrotasks.
- Microtasks have higher priority than macrotasks (e.g.,
-
Complex Scenario
setTimeout(() => console.log("Timeout 1"), 0); Promise.resolve().then(() => { console.log("Promise 1"); setTimeout(() => console.log("Timeout 2"), 0); }); Promise.resolve().then(() => console.log("Promise 2"));- Output Order:
- After synchronous code execution, the microtask queue contains two
Promise.thencallbacks. - Output "Promise 1" → "Promise 2" in sequence (microtask queue cleared).
- Execute macrotasks: First output "Timeout 1", then execute the second
setTimeout(registered in the microtask) and output "Timeout 2".
- After synchronous code execution, the microtask queue contains two
- Output Order:
-
Common Pitfalls
- Recursively adding microtasks within a microtask can block the main thread (as the event loop will continuously clear the microtask queue).
- The delay time for macrotasks (e.g.,
setTimeout) is the minimum waiting time and does not guarantee precise execution.
By understanding these steps, you can predict the execution order of asynchronous code and avoid common concurrency issues.