Modern ES6+ & Async


101) What problem did ES6 Modules solve compared to old scripts?

Answer: Old scripts dump everything into a shared global space (easy to collide, hard to reason). ES Modules give file-level scope , explicit import/export, and let bundlers/tree-shaking remove unused code.


102) import vs require() — what’s the real difference?

Answer: import is static (known at parse time), enabling tree-shaking and faster analysis. require() is dynamic (runs at runtime), which makes optimization harder and can load conditionally.


103) Why can’t you import inside an if in normal ESM syntax?

Answer: Because import must be statically analyzable. If you need conditional loading, use dynamic import :

if (flag) {
  const mod = await import("./heavy.js");
}

104) What’s the difference between default export and named export ?

Answer:

  • Default: one “main thing” per file, import name is flexible

  • Named: multiple exports, import name must match (or alias)


105) What is “tree-shaking” in simple terms?

Answer: It’s “dead code removal” for modules. Because import/export is static, bundlers can detect unused exports and drop them from production builds .


106) What is destructuring and why do interviewers like it?

Answer: It’s a clean way to unpack values. It reduces boilerplate and makes intent obvious.


107) Destructuring: what’s the “gotcha” with missing properties?

Answer: Missing properties become undefined, so defaults help:


108) How do destructuring defaults differ from || defaults?

Answer: Destructuring default triggers only when value is undefined , not when it’s 0 or "". || triggers on any falsy value.


109) What is the spread operator ... really doing in arrays?

Answer: It copies elements into a new array (shallow). Great for immutability patterns.


110) Spread on objects: what’s the important detail?

Answer: Object spread copies own enumerable properties (shallow). If nested objects exist, both copies still point to the same nested references.


111) Rest parameters (...args) vs arguments — why is rest better?

Answer: Rest is a real array, works in arrow functions, and is clearer. arguments is array-like and has legacy quirks.


112) What are template literals beyond “string interpolation”?

Answer: They preserve multi-line strings and allow tagged templates (advanced), but the key win is fewer concatenation bugs:


113) What is a Promise in one line?

Answer: A Promise is a container for a future value : pending → fulfilled (value) or rejected (error).


114) Why do Promises feel different from callbacks?

Answer: Promises give you composition (chain/merge) and error propagation (like try/catch but async-friendly). Callbacks often create “pyramid code” and scattered error handling.


115) What are the 3 states of a Promise and why does it matter?

Answer: pending / fulfilled / rejected. Once fulfilled or rejected, it’s settled forever (cannot change again), which makes async flows predictable.


116) What’s the difference between .then() and .catch()?

Answer: .then() handles success (and can also handle failure if you pass 2nd arg). .catch() is .then(null, handler) and catches rejections in the chain.


117) Why does a .then() return a new Promise?

Answer: Because chaining must represent the next step’s future value. Whatever you return becomes the next promise’s resolution.


118) What happens if you return a Promise inside .then()?

Answer: The chain waits for it (promise “flattens”). This is why chaining works nicely.


119) What happens if you throw inside .then()?

Answer: It becomes a rejection for the next promise. That’s how errors flow down the chain.


120) What’s finally() for?

Answer: Cleanup. Runs whether success or failure, but it doesn’t receive the value/error as a parameter (and returning value usually doesn’t override unless you throw).


121) Promise microtask — why do promises run “before timeouts”?

Answer: Promise callbacks go into the microtask queue , which the event loop drains before the next macrotask (setTimeout). That’s why promises feel “more immediate”.


122) async/await is “syntactic sugar”… but for what exactly?

Answer: For Promises + chaining. await pauses the async function until the promise settles, but it does not block the whole thread —it yields back to the event loop.


123) What does an async function always return?

Answer: A Promise.

  • return value → resolves

  • throw error → rejects


124) Why does this work with try/catch?

Answer: Because await turns a rejection into a thrown error within that async function , so normal try/catch logic applies.


125) What’s a common mistake with await inside loops?

Answer: Doing it sequentially by accident. This is slow:

If independent, do parallel:


126) Promise.all — what’s the behavior interviewers test?

Answer: It runs promises in parallel and fails fast : if one rejects, the whole thing rejects (others may still be running in background).


127) Promise.allSettled — when do you prefer it?

Answer: When you want results from all operations even if some fail (e.g., loading multiple widgets, batch API calls).


128) Promise.race vs Promise.any — what’s the difference?

Answer:

  • race: first to settle wins (resolve or reject)

  • any: first to resolve wins; rejects only if all reject (throws AggregateError)


129) How would you implement a timeout for a promise?

Answer: Race the promise with a timer promise.


130) What is optional chaining ?. doing under the hood?

Answer: It prevents “crash on null/undefined”. If left side is nullish, expression short-circuits to undefined.


131) What is the nullish coalescing operator ?? good for?

Answer: It’s a safer default operator. Only falls back on null or undefined, not on 0 or "".


132) What’s the difference between || and ?? in practice?

Answer: price || 10 breaks when price is 0. price ?? 10 keeps 0.


133) What are iterators in simple terms?

Answer: An iterator is an object with a next() method that returns { value, done }. It’s how JS powers for...of.


134) What are iterables?

Answer: Anything that has [Symbol.iterator]() returning an iterator—like arrays, strings, maps, sets.


135) Why does for...of work on arrays but not plain objects?

Answer: Arrays are iterable by default. Plain objects aren’t iterable unless you implement [Symbol.iterator].


136) How does a generator function work conceptually?

Answer: It’s a function that can pause (yield) and later resume, producing values one by one—like a vending machine: call next() to get the next snack.


137) What does yield actually do?

Answer: It returns a value outward and pauses the generator’s execution state so it can continue later from the same point.


138) What’s the difference between yield and return inside generators?

Answer: yield pauses with {done:false}. return ends the generator with {done:true}.


139) Why are generators useful in real apps?

Answer: Lazy sequences (infinite streams), custom iteration, and complex async flow control (historically before async/await). Also used in libraries like Redux-Saga.


140) Explain for await...of in one “why it exists” sentence.

Answer: It lets you iterate over async iterables (streams of promises) in a clean loop—useful for reading streams or consuming paginated async data.


141) What’s the “gotcha” with Array.prototype.forEach and await?

Answer: forEach doesn’t await async callbacks. Your loop won’t wait.

Bad:

Good:


142) What is a “thenable” and why does it matter?

Answer: Any object with a .then method. Promises will “adopt” thenables, which is great for interop but can be risky if .then is weird/malicious.


143) What are dynamic imports good for in performance?

Answer: Code-splitting: load heavy features only when needed (route-based loading, admin panel, charts).


144) What’s top-level await and when should you avoid it?

Answer: Awaiting directly in a module. Useful for bootstrapping config, but it can delay module initialization and impact startup/perf if overused.


145) What is Symbol used for in day-to-day engineering?

Answer: Creating unique keys (no collisions), hiding internal properties, and implementing protocols like iterables (Symbol.iterator) or primitive conversion (Symbol.toPrimitive).


146) What’s the difference between Map and Set (ES6 structures)?

Answer: Map stores key→value. Set stores unique values only. Both have better semantics than using arrays for uniqueness or objects for maps.


147) Why is Map often preferred over {} for dictionaries?

Answer: Any key type, predictable iteration, no accidental prototype collisions, and better APIs (size, has, set, delete).


148) What’s the difference between shallow and deep copy in ES6+ tools?

Answer: Spread, Object.assign, and Array.slice are shallow. Deep copy needs recursion, structured clone, or libraries (and must handle Dates, Maps, cycles, etc.).


149) What is the structured clone idea (conceptually)?

Answer: It’s “deep copy that understands real JS types” (like Maps/Sets) and handles cycles, used in platforms like postMessage / some runtimes (structuredClone).


150) If async/await is so nice, why should you still understand Promises?

Answer: Because async/await is built on Promises: you still need Promises for parallel patterns (all, race), interoperability, performance decisions, and debugging microtask timing.


Say “Next” for Batch 4 (151–200): Web APIs & Performance (DOM, event delegation, debounce/throttle, memory leaks, security: XSS/CORS).

Last updated