JavaScript Coding Interview Patterns
High-yield JS coding patterns for FE interviews — debounce, async control, arrays, caches, emitters — with complexity and practice links.
- interview
- javascript-coding
Frontend coding rounds recycle a small pattern set. Master the patterns with clean APIs and edge cases; random problem grinding without transfer is slow.
Pair with JavaScript interview guide for verbal topics.
Pattern map
| Pattern | Typical prompt | Site deep dive |
|---|---|---|
| Debounce / throttle | Search input, scroll | Debounce |
| Promise concurrency | all / pool / retry |
Promises |
| Event emitter | pub/sub | Event emitter |
| Cache / memo | LRU, memoize | LRU |
| Array transforms | map/filter/flat | map filter reduce |
| Two pointers / window | strings, subarrays | Longest substring |
| Trees / nested | flatten, traverse | Deep vs shallow |
| DOM / events | delegation | Event delegation |
1. Debounce / throttle
Clarify: leading vs trailing? maxWait? cancel?
function debounce(fn, wait) {
let t = null;
function debounced(...args) {
clearTimeout(t);
t = setTimeout(() => fn.apply(this, args), wait);
}
debounced.cancel = () => clearTimeout(t);
return debounced;
}
Mention: React state → prefer debouncing the event or using a debounced value hook; cleanup on unmount. Complexity: O(1) per call.
2. Promise utilities
all (fail-fast) vs allSettled
Know: Promise.all rejects on first failure; allSettled waits for all.
Retry with backoff + abort
async function fetchWithRetry(url, { retries = 3, signal } = {}) {
let lastErr;
for (let i = 0; i <= retries; i++) {
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
try {
const res = await fetch(url, { signal });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} catch (e) {
lastErr = e;
if (e.name === "AbortError") throw e;
await new Promise((r) => setTimeout(r, 2 ** i * 100));
}
}
throw lastErr;
}
Related: AbortController · Fetch fundamentals.
Concurrency pool
Prompt: “Download n URLs with at most k in flight.” Queue + active count; O(n) tasks, k workers.
3. Event emitter
class Emitter {
constructor() {
this.map = new Map(); // event -> Set<fn>
}
on(event, fn) {
if (!this.map.has(event)) this.map.set(event, new Set());
this.map.get(event).add(fn);
return () => this.off(event, fn);
}
off(event, fn) {
this.map.get(event)?.delete(fn);
}
emit(event, ...args) {
for (const fn of [...(this.map.get(event) || [])]) fn(...args);
}
}
Footguns: mutate listener set while emitting; once; error in listener shouldn’t break others (policy).
4. Array / string patterns
| Pattern | Use |
|---|---|
| Frequency map | anagrams, top-k prep |
| Sliding window | longest substring, min window |
| Two pointers | sorted pair sums, palindromes |
| Prefix sums | range queries |
| Stack | valid parentheses, monotonic stack |
DSA reps: Two sum · Valid parentheses · Group anagrams.
5. Flatten & deep clone
function flatten(arr) {
const out = [];
for (const x of arr) {
if (Array.isArray(x)) out.push(...flatten(x));
else out.push(x);
}
return out;
}
Clone: structuredClone for most data; know cycle and function limitations — Deep vs shallow copy.
6. Curry / compose (sometimes)
const compose =
(...fns) =>
(x) =>
fns.reduceRight((v, f) => f(v), x);
See Compose and pipe · Currying.
7. Polyfill-style prompts
Promise.allArray.prototype.map(sparse arrays!)Function.prototype.bindJSON.stringifylite
Interviewers watch edge cases more than cleverness.
Complexity talk
Always end with time/space. Guide: Complexity analysis for JS engineers.
Practice set (write under timer)
- Debounce with cancel + flush
Promise.all- Emitter with
once - LRU cache
- Deep equality
- Flatten with depth param
- Throttle trailing
- Retry fetch with AbortSignal
Out-loud checklist
- Function signature first
- Examples / edge cases
- Working brute if needed
- Clean names, no golf
- Complexity + one production note
Related on this site
Further reading
Patterns transfer. When you see a new prompt, name the pattern first — the implementation follows.