JavaScript Scenario-Based Quiz for 2025 Quiz

Test your knowledge of essential JavaScript scenarios with this easy, interview-oriented quiz. Assess your understanding of JavaScript event loop, microtasks, memory leaks, and best practices—all relevant for developers preparing for 2025 challenges.

  1. Microtasks and UI Flicker

    When a JavaScript single-page app shows UI flicker after users click quickly, what is the likely cause related to microtasks and macrotasks?

    1. Event listeners are automatically removed on rapid clicks
    2. Microtasks are starving rendering by delaying macrotasks
    3. Macro tasks always finish before microtasks, causing state loss
    4. Timers are not firing due to garbage collection

    Explanation: Microtasks (like Promises) are executed before macrotasks (like setTimeout), so if too many microtasks run, they can delay the browser’s chance to render, creating UI flicker. Timers not firing would typically result in missed events rather than flicker. Macro tasks do not finish before microtasks; microtasks always run first. Event listeners are not removed automatically, so the fourth option is incorrect.

  2. Node.js Event Loop Blocking

    If a Node.js HTTP server experiences CPU spikes during peak traffic even though there are few I/O operations, what should you check first?

    1. Switch all callbacks to Promises
    2. Look for synchronous JavaScript blocking the event loop
    3. Disable caching to free up memory
    4. Increase the number of open database connections

    Explanation: Synchronous JavaScript can block the Node.js event loop, causing CPU to spike and latency to increase. Increasing database connections won’t help if the code is blocked. Simply switching to Promises does not resolve blocking if the logic is still synchronous. Disabling caching may impact performance but won't solve CPU event loop blocking.

  3. Optional Chaining Usage

    In which code area should optional chaining (?.) generally be avoided for best performance?

    1. Feature flag access with possible null
    2. Tight inner loops with guaranteed data shape
    3. Configuration file reading
    4. API response parsing

    Explanation: Optional chaining is best used when values might be absent, such as incoming API data, configs, or feature flags. In tight inner loops where data is validated and guaranteed, using optional chaining can hide bugs and reduce performance slightly. The other options represent scenarios where optional chaining improves safety and code clarity.

  4. Timer vs Promise Ordering

    After moving to ES modules, you notice a setTimeout callback runs after a Promise thenable. Why does this happen?

    1. setTimeout callbacks have higher priority than Promise chains
    2. Microtasks always run before macrotasks
    3. ES modules reverse callback queue order
    4. Promise callbacks are ignored by browsers

    Explanation: JavaScript always finishes all microtasks (such as resolved Promises) before handling the next macrotask (like setTimeout). setTimeout does not run before a microtask; the opposite is true. ES modules do not affect callback queue order. Promise callbacks are never ignored by browsers under normal operation.

  5. Detecting Memory Leaks

    Which is a common source of memory leaks in JavaScript web applications after long user sessions?

    1. CSS hover effects in stylesheets
    2. Variables declared with let in a function
    3. Event listeners attached to detached DOM nodes
    4. Removing objects from global scope

    Explanation: Attaching event listeners to DOM nodes that are later removed from the DOM but not properly cleaned up can cause memory leaks. Variables declared with let are local and cleaned up when the function ends. CSS hover effects are not related to JavaScript memory. Removing objects from global scope helps, rather than hurts, memory usage.

  6. Handling Asynchronous Heavy Work in Node.js

    If a function in a Node.js service is doing heavy computation that blocks the event loop, what is an effective solution?

    1. Add more event listeners to the process
    2. Use eval instead of normal functions
    3. Move the computation to a worker or child process
    4. Increase the number of async callbacks

    Explanation: Offloading heavy work to a worker or child process allows the main event loop to stay responsive. Increasing callbacks won’t help if the work remains synchronous. Adding more event listeners increases memory overhead but does not solve blocking. Using eval is unrelated and introduces security and performance issues.

  7. fixing UI State Flicker

    You notice UI state flicker when multiple setTimeout and Promise callbacks are used together in a component. What is a good approach to fix this?

    1. Disable user events during rendering
    2. Add more global variables
    3. Batch state changes into a single microtask
    4. Chain setTimeouts repeatedly

    Explanation: Batching state changes inside a single microtask ensures predictable updates and reduces flicker. Chaining setTimeouts can make the issue worse by increasing the unpredictability of updates. Adding global variables is not a solution and can introduce bugs. Disabling user events hinders usability and is not necessary for flicker management.

  8. Understanding Nullish Coalescing

    Which operator should you combine with optional chaining to provide a fallback value when accessing nested properties?

    1. The nullish coalescing operator (??)
    2. The logical OR operator (||) for all data
    3. The bitwise AND operator (u0026)
    4. The not-equals operator (!=)

    Explanation: The nullish coalescing operator (??) returns the right-hand value when the left is null or undefined, making it a good companion for optional chaining. The bitwise AND operator does not deal with undefined or null. Logical OR (||) sometimes produces unexpected results if valid falsy values like 0 or empty strings are needed. The not-equals operator is not suitable for value selection.

  9. Clearing Timers on Route Change

    Why should you always clear timers and intervals in JavaScript when navigating between pages or components?

    1. Because intervals are automatically cleared when you navigate
    2. To avoid accidental memory leaks and repeated code execution
    3. Because timers slow down page transitions
    4. To make sure styles apply correctly

    Explanation: Timers and intervals, if not cleared, can keep running and reference old data, leading to memory leaks and unwanted repeated execution. Timers alone are not the cause of slow transitions. Clearing them has no effect on styles. Intervals are not cleared automatically by navigation; manual cleanup is required.

  10. Testing Timer and Microtask Order

    Which testing practice helps avoid broken tests after refactoring timer and Promise code in JavaScript modules?

    1. Disable microtask queues in testing environments
    2. Test only by assuming timers always fire immediately
    3. Hardcode the execution order of callbacks
    4. Write tests to await Promises produced by your code

    Explanation: Awaiting Promises produced by your code provides explicit control over async behavior and avoids assumptions about scheduling. Assuming timers always fire immediately is unreliable and not true across all environments. Hardcoding callback order is brittle and makes tests less robust. Disabling microtasks is not practical or generally available in test tools.

  11. Optimizing Global Cache

    How can you prevent a global cache object from causing memory leaks in a JavaScript application?

    1. Increase object references within the cache
    2. Store all cache values as strings
    3. Set size limits and eviction policies on the cache
    4. Use var declarations for cache keys

    Explanation: Imposing size limits and eviction policies keeps the cache from growing uncontrollably and consuming memory. Increasing object references compounds leaks. Choice of `var` for cache keys does not impact memory management. Storing cache values as strings doesn’t prevent memory leaks if unused entries are never removed.

  12. Safe Use of Observers

    To avoid memory leaks, what should you do with MutationObserver or IntersectionObserver instances when a component is destroyed?

    1. Rely on garbage collection to handle it
    2. Remove observers from the global array
    3. Disconnect the observer instances
    4. Create new observers instead

    Explanation: Disconnecting observers releases their references and callbacks, preventing leaks after components are destroyed. Simply creating new observers does not clean up the old ones. Garbage collection may not help if references remain active. Removing from arrays does not affect the observer’s attachment to the DOM.

  13. Closures and Large References

    Why should you be cautious about closures that capture large objects in JavaScript?

    1. They can unintentionally keep those objects in memory longer than needed
    2. Closures cause syntax errors if too large
    3. Large objects make your code run faster
    4. Closures are invalid inside loops

    Explanation: Closures hold onto referenced variables as long as the closure exists, which is problematic with large objects. Syntax errors are unrelated to closure size. Large objects generally slow down code, not speed it up. Closures are valid inside loops; the problem is retention, not syntax.

  14. Validating Optional Values

    What is a robust practice before accessing deeply nested optional data in JavaScript?

    1. Force all values to be non-null with type casting
    2. Add optional chaining everywhere
    3. Validate objects once, then access directly in inner loops
    4. Access everything with bracket notation only

    Explanation: Validating data once upfront ensures safety and avoids unnecessary repeated checks, leading to better performance in loops. Adding optional chaining everywhere adds redundant overhead. Bracket notation does not protect against undefined references. Forcing type casts can hide errors if data is not valid.

  15. Process.nextTick and Event Loop Starvation

    Abuse of which method can starve the event loop phases in Node.js, blocking timers and I/O callbacks?

    1. setInterval
    2. process.nextTick
    3. require
    4. console.log

    Explanation: Overusing process.nextTick can prevent the event loop from handling timers and I/O by flooding the next tick queue. setInterval schedules repeated work but doesn’t directly starve the loop. console.log affects performance if overused but does not block the event loop. require is used for imports and not related to event loop starvation.

  16. Scheduling for Paint-Aligned Work

    Which JavaScript scheduling method best aligns work with the browser's next paint for smoother UI updates?

    1. setTimeout with zero delay
    2. requestAnimationFrame
    3. Promise.resolve with then
    4. setInterval

    Explanation: requestAnimationFrame schedules a callback right before the next browser paint, making it ideal for smooth visual updates. setTimeout with zero delay does not align with rendering timing precisely. setInterval is for repeated intervals and not meant for animation pacing. Promise.resolve with then runs microtasks, which always occur before rendering.