JavaScript
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
55: The Event Loop Explained
Why doesn't setTimeout(callback, 0) actually run immediately?
This is usually the first "wait, what?" moment for most developers. You'd think telling JavaScript to wait 0 milliseconds means "do this right now," but that's not how the engine works. JavaScript is single-threaded, meaning it can only do one thing at a time. It has a Call Stack, and as long as there is something on that stack, the engine won't look anywhere else.
When you call setTimeout, you aren't actually telling JS to run that function in 0ms; you're telling the browser (the Web API environment) to start a timer and, once it's done, push the callback function into the Task Queue. The Event Loop's only job is to look at the Call Stack and the Task Queue. If the stack is empty, it pushes the first thing from the queue onto the stack.
console.log('Start');
setTimeout(() => {
console.log('Timer finished');
}, 0);
console.log('End');
// Output:
// Start
// End
// Timer finished
In the example above, the setTimeout callback is sent to the queue, but it has to wait its turn. The "End" log is still on the main execution stack, so the Event Loop can't move the timer callback up until the script has completely finished running.
What's the deal with Microtasks versus Macrotasks?
If you start working with Promises, you'll notice they behave differently than setTimeout. This is because JavaScript maintains two different queues: the Macrotask Queue (where timers and I/O live) and the Microtask Queue (where Promise callbacks and MutationObserver live).
Here is the golden rule: The Event Loop will prioritize the entire Microtask Queue over the Macrotask Queue. It will not even look at the next setTimeout until every single pending microtask has been cleared. I've seen people accidentally create infinite loops by recursively calling microtasks, which completely freezes the browser because the Macrotask queue (which handles things like UI rendering) never gets a turn.
console.log('Script start');
setTimeout(() => {
console.log('Macrotask: Timeout');
}, 0);
Promise.resolve().then(() => {
console.log('Microtask: Promise 1');
}).then(() => {
console.log('Microtask: Promise 2');
});
console.log('Script end');
// Output:
// Script start
// Script end
// Microtask: Promise 1
// Microtask: Promise 2
// Macrotask: Timeout
Notice how the Promise chain finishes entirely before the timeout fires, even though the timeout was scheduled "first."
If JS is single-threaded, how does it handle async work without freezing?
It's a common misconception that JavaScript "does" the async work. It doesn't. The JavaScript engine (like V8) just handles the execution. The actual waiting—whether it's a network request via fetch, a timer, or listening for a click—happens outside the engine in the browser's Web APIs (or in Node.js's C++ APIs).
Think of the JS engine as a chef in a kitchen. The chef can't chop vegetables and wait for the oven to preheat at the same time. Instead, the chef tells the oven (the Web API), "Let me know when you're at 400 degrees," and goes back to chopping. When the oven beeps, it places a "Notification" in the Task Queue. The chef finishes the current vegetable they are chopping (clearing the stack) and then checks the queue to see that the oven is ready.
This is why your UI doesn't freeze when you fetch data from an API. The request is handled by the browser's network stack in the background. Your JS code just provides the "callback" (via .then() or await) that the Event Loop will eventually execute once the response comes back.
📋 Practical Task
Exercise: The Execution Order Predictor
You are debugging a piece of legacy code where the execution order is critical. Your goal is to modify the provided code so that the "Final Cleanup" log happens last, and the "Data Processed" log happens before the "UI Updated" log.
Currently, the code is out of order. You must move the logic into the correct types of asynchronous tasks (Microtasks vs Macrotasks) to achieve the desired sequence.
// CURRENT BROKEN CODE:
console.log('Application Start');
setTimeout(() => {
console.log('UI Updated');
}, 0);
Promise.resolve().then(() => {
console.log('Final Cleanup');
});
setTimeout(() => {
console.log('Data Processed');
}, 0);
console.log('Application End');
/*
DESIRED OUTPUT:
Application Start
Application End
Data Processed
UI Updated
Final Cleanup
*/
Requirements:
- You cannot change the text inside the
console.logstatements. - You must use a combination of
Promise.resolve().then()andsetTimeout()to manipulate the priority. - Ensure "Final Cleanup" is the absolute last thing to print.
There are no comments for now.