The Event Loop is a mechanism used by browsers to handle asynchronous events. It ensures that JavaScript execution appears synchronous, even though JavaScript is single-threaded. Below is the workflow of the event loop:
- Call Stack: When a JavaScript code segment begins execution, it is first added to the call stack. If the code is a function, it is placed at the top of the stack.
- Web APIs: When asynchronous operations (such as
setTimeout,XMLHttpRequest, etc.) are encountered, they are handled by the browser's Web APIs. The call stack continues executing the next line of code without waiting for the asynchronous operation to complete. - Task Queue: Once the asynchronous operation completes (e.g., the specified time has passed for
setTimeout), the callback function is added to the task queue. The task queue is a list of callback functions waiting to be executed after the call stack is empty. - Event Loop: The event loop monitors the call stack and task queue. If the call stack is empty, it checks the task queue. If there are pending callback functions in the task queue, the event loop retrieves them from the queue and pushes them onto the call stack for execution.
- Render Queue: When the browser is ready to render (typically every 16.7 milliseconds, corresponding to 60fps), it has its own render queue to handle repaint and reflow events. If both the call stack and task queue are empty, the event loop retrieves tasks from the render queue for execution to ensure the user interface updates promptly.
- Microtask Queue: In addition to the regular task queue, microtasks (e.g., Promise callbacks) exist. The microtask queue is processed immediately after the current call stack is cleared, even if tasks are pending in the task queue. The event loop checks the task queue only after the microtask queue is empty.
Example
Suppose we have the following code snippet:
javascriptconsole.log('Script starts'); setTimeout(function() { console.log('setTimeout'); }, 0); Promise.resolve().then(function() { console.log('promise1'); }).then(function() { console.log('promise2'); }); console.log('Script ends');
The execution order will be as follows:
'Script starts'is printed to the console because it is the first line of synchronous code.- The callback for
setTimeoutis handled by Web APIs, and the call stack continues executing the next line of code. Promise.resolve()creates a Promise, and its callback is added to the microtask queue.'Script ends'is printed to the console because it is synchronous code.- The current synchronous code has finished execution, and the call stack is cleared.
- The event loop first checks the microtask queue and finds Promise callbacks.
'promise1'and'promise2'are printed to the console in sequence.- The microtask queue is emptied, and the event loop now checks the task queue.
- The callback for
setTimeoutis moved from the task queue to the call stack by the event loop and printed as'setTimeout'.