Back to Insights
Interview Prep

Top Advanced JavaScript Interview Questions for Senior Engineers (2026)

2026-01-235 min read

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:

  1. Synchronous Code: 1 and 5 are logged first because they are on the main Call Stack.
  2. Microtask Queue: Promises (.then) and queueMicrotask allow tasks to run immediately after the current script executes but before the Event Loop continues to the next task. Hence, 3 and 4 run next.
  3. Macrotask Queue: setTimeout represents a Macrotask. The Event Loop processes the entire Microtask Queue before moving to the first task in the Macrotask Queue. Thus, 2 is 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:

  1. Handling non-promises: Using Promise.resolve(promise) ensures values like 42 or strings work correctly.
  2. Order preservation: results[index] = value ensures the output array matches the input order, regardless of which promise finishes first.
  3. Fail-fast: Calling reject immediately 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 X milliseconds 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!

Written byAshok Kumar Sand
Discuss this topic