In 2026, the bar for Senior Frontend Engineers has risen significantly. Interviewers are moving away from trivia ("What is hoisting?") and focusing on execution context, memory management, and asynchronous orchestration.
If you are aiming for a Principal or Senior role, you need to understand how the runtime works, not just how to use syntax. Here are the top advanced JavaScript interview questions I ask candidates, along with the deep-dive answers expected at the senior level.
1. The Event Loop: Microtasks vs. Macrotasks
Question: What is the output of the following code, and strictly explain the order of execution in the Event Loop?
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
queueMicrotask(() => console.log('4'));
console.log('5');
Answer:
1
5
3
4
2
The Senior Explanation:
- Synchronous Code:
1and5are logged first because they are on the main Call Stack. - Microtask Queue: Promises (
.then) andqueueMicrotaskallow tasks to run immediately after the current script executes but before the Event Loop continues to the next task. Hence,3and4run next. - Macrotask Queue:
setTimeoutrepresents a Macrotask. The Event Loop processes the entire Microtask Queue before moving to the first task in the Macrotask Queue. Thus,2is last.
2. Stale Closures in React (and Vanilla JS)
Question: Why does this function log 0 even after we increment the value? How would you fix it?
function createLogger() {
let count = 0;
const increment = () => { count++; };
const log = () => {
setTimeout(() => {
console.log(`Count is: ${count}`);
}, 1000);
};
return { increment, log };
}
// Scenario:
const logger = createLogger();
logger.increment();
logger.log(); // Logs "Count is: 1" - Wait, this works.
// BUT... what if we destructure?
Note: In plain JS, references are usually maintained. The "Stale Closure" problem is prevalent in React useEffect or useCallback.
React Context Question:
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
console.log(count); // Always logs 0! Why?
}, 1000);
return () => clearInterval(timer);
}, []); // Empty dependency array
return <button onClick={() => setCount(c => c + 1)}>+</button>;
}
The Senior Explanation:
The closure inside setInterval captured the count variable from the first render (when it was 0). Because the effect dependency array [] is empty, the effect never re-runs, and the closure is never recreated with the updated count.
Fix: Add [count] to the dependency array or use a ref (useRef) to hold the mutable value if you don't want to re-subscribe.
3. Implementing Promise.all Polyfill
Question: Write a polyfill for Promise.all. It should fail if any promise rejects (fail-fast behavior).
Answer:
function myPromiseAll(promises) {
return new Promise((resolve, reject) => {
let results = [];
let completed = 0;
if (promises.length === 0) {
resolve(results);
return;
}
promises.forEach((promise, index) => {
Promise.resolve(promise)
.then((value) => {
results[index] = value;
completed++;
if (completed === promises.length) {
resolve(results);
}
})
.catch((err) => {
reject(err); // Fail fast logic
});
});
});
}
What I look for:
- Handling non-promises: Using
Promise.resolve(promise)ensures values like42or strings work correctly. - Order preservation:
results[index] = valueensures the output array matches the input order, regardless of which promise finishes first. - Fail-fast: Calling
rejectimmediately upon error.
4. Debounce vs. Throttle (System Design)
Question: You are building a search bar that fetches data from an API. Do you use Debounce or Throttle? Why?
Answer:
- Use Debounce.
- Why: You want to wait for the user to stop typing before firing the request. Throttle would fire a request every
Xmilliseconds while they are typing, which is unnecessary load for a search input.
Follow up: When would you use Throttle?
- Answer: Infinite scrolling or window resizing events. You want to check the scroll position periodically (every 100ms) to trigger a "load more" action, rather than waiting for the user to stop scrolling completely.
5. Prototypal Inheritance vs. Class Inheritance
Question: Explain how TypeScript/ES6 classes work under the hood.
The Senior Explanation:
JavaScript does not have "real" classes like Java or C#. class is syntactic sugar over Prototypal Inheritance.
When you write class Dog extends Animal, JavaScript is actually linking the prototype of Dog to Animal.
Dog.prototype.[[Prototype]] = Animal.prototype;
This means lookups travel up the prototype chain. Understanding this is crucial for debugging weird inheritance issues or performance tuning (shared methods live on the prototype, not the instance).
Final Thoughts for the Interview
When you answer, don't just dump the code. Walk through your mental model. Explain why the Event Loop prioritizes microtasks, or why closures retain memory. That is what separates a Senior Engineer from a Junior.
Good luck!