console.log('1');
setTimeout(() => console.log('2'), 0);
console.log('3');
// logs: 1, 3, 2
Topics
37
ArrayBuffer, TypedArrays & Binary Data
Arrays & Array Methods
Async Iterators & Streams
Browser Storage & Web APIs
Classes & Class Syntax
Date, Time & Internationalization (Intl API)
Debugging, Testing & Tooling
Design Patterns in JavaScript
Destructuring, Spread & Rest
DOM & Events
Error Handling
ES Modules
Event Loop & Concurrency
Functional Programming
Iterators & Generators
JSON & Data Serialization
Map, Set, WeakMap & WeakSet
Memory Management & Garbage Collection
Networking: Fetch, XHR, WebSockets & CORS
Numbers, Math & BigInt
Objects, Property Descriptors & Immutability
Optional Chaining & Nullish Coalescing
Package Management, Bundlers & Transpilation (npm, Webpack/Vite, Babel)
Performance Optimization: Debouncing, Throttling & Memoization
Promises & async/await
Prototypes & Inheritance
Proxy & Reflect
Regular Expressions
Scope, Hoisting & Closures
Security: XSS, CSRF & Content Security Policy
Service Workers & Progressive Web Apps
Strings & Template Literals
Symbols & Well-Known Symbols
this & Binding
Types & Coercion
Web Components & Custom Elements
Web Workers & Multithreading
Event Loop & Concurrency
10 questions found
Yes, JavaScript runs on a single main thread, executing one operation at a time. It achieves the appearance of doing multiple things at once through asynchronous callbacks, Promises, and the event loop — not through true parallel execution on that thread.
Real-world example
Understanding why a long synchronous loop freezes the entire page, including scrolling and clicks.
Web Workers & Multithreading
The call stack tracks which function is currently executing and what called it, growing with each function call and shrinking as functions return — a 'stack overflow' happens when it grows too deep, usually from unbounded recursion.
function a() { b(); }
function b() { c(); }
function c() { console.log('deepest'); }
a(); // stack: a -> b -> c, then unwinds
Real-world example
Reading a stack trace in an error to see the exact chain of function calls that led to a crash.
Error Handling
Promise callbacks (.then, async/await continuations) go into the microtask queue, which is fully drained after every single macrotask completes. setTimeout, setInterval, and I/O callbacks go into the macrotask queue, processed one at a time between event loop ticks.
console.log('1');
setTimeout(() => console.log('2 (macrotask)'), 0);
Promise.resolve().then(() => console.log('3 (microtask)'));
console.log('4');
// logs: 1, 4, 3, 2
Real-world example
Understanding why a Promise callback always runs before a setTimeout(fn, 0), even though both are 'async'.
Promises & async/await
After each synchronous script (or a single macrotask) finishes, the event loop first fully drains the microtask queue, then renders any pending UI updates, then picks up the next macrotask from the queue — repeating this cycle continuously.
// Simplified loop:
// while (true) {
// runOneMacrotask();
// drainAllMicrotasks();
// renderIfNeeded();
// }
Real-world example
Explaining why UI updates can appear to 'batch' after several synchronous state changes but before the next repaint.
Async Iterators & Streams
Since JS runs on one thread shared with rendering and input handling, any synchronous code that takes a long time to finish prevents the browser from processing clicks, repainting the screen, or running queued callbacks until that function returns.
function blockFor(ms) {
const end = Date.now() + ms;
while (Date.now() < end) {} // busy-waits, freezing the page
}
blockFor(3000); // UI is frozen for 3 seconds
Real-world example
Diagnosing a frozen 'Not Responding' UI caused by a synchronous JSON.parse() on a huge payload.
Performance Optimization: Debouncing
Throttling & Memoization
setTimeout(fn, 0) doesn't run immediately — it's still scheduled as a macrotask, which the browser also clamps to a minimum delay (often ~4ms after nesting). Promise callbacks are microtasks, and ALL pending microtasks are drained before the event loop even considers the next macrotask.
setTimeout(() => console.log('macro'), 0);
Promise.resolve().then(() => console.log('micro'));
// logs: 'micro' then 'macro', every time
Real-world example
A subtle bug where code assumed setTimeout(fn, 0) would run 'immediately after' the current synchronous code, before any promises.
Promises & async/await
Split the work into smaller batches and yield control back to the event loop between them using setTimeout(fn, 0) or requestIdleCallback(), allowing pending UI updates and user input to be processed between chunks.
function processInChunks(items, chunkSize, fn) {
let i = 0;
function next() {
const chunk = items.slice(i, i + chunkSize);
chunk.forEach(fn);
i += chunkSize;
if (i < items.length) setTimeout(next, 0);
}
next();
}
Real-world example
Processing 100,000 rows of imported CSV data client-side without freezing the browser tab.
Performance Optimization: Debouncing
Throttling & Memoization
Because ALL pending microtasks are drained before the next macrotask runs, code that keeps scheduling new microtasks from within a microtask (e.g. a Promise chain that queues another .then() indefinitely) can starve macrotasks — including rendering and setTimeout callbacks — from ever running.
function foreverMicrotask() {
Promise.resolve().then(foreverMicrotask); // never lets a macrotask run
}
foreverMicrotask(); // freezes rendering indefinitely
Real-world example
Debugging a page that becomes completely unresponsive due to a recursive .then() chain with no exit condition.
Promises & async/await
An async function returns immediately at its first await, suspending execution; the code after await is scheduled to resume as a microtask once the awaited Promise settles — functionally equivalent to chaining .then() callbacks, just with more readable syntax.
async function run() {
console.log('1');
await null; // yields, resumes as a microtask
console.log('2');
}
run();
console.log('3');
// logs: 1, 3, 2
Real-world example
Understanding why code immediately after an awaited call doesn't run synchronously, even for an already-resolved value.
Promises & async/await
requestAnimationFrame schedules a callback to run right before the browser's next repaint, after microtasks have drained — making it the right tool for visual updates synced to the display's refresh rate, unlike setTimeout which has no such guarantee.
function animate() {
element.style.left = `${position++}px`;
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
Real-world example
Building a smooth custom animation that stays in sync with the browser's actual repaint cycle, avoiding jank.
Performance Optimization: Debouncing
Throttling & Memoization