ESC

Type to search the knowledge base.

JavaScript Coding Interview Patterns

High-yield JS coding patterns for FE interviews — debounce, async control, arrays, caches, emitters — with complexity and practice links.

intermediate4 min read
  • 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.all
  • Array.prototype.map (sparse arrays!)
  • Function.prototype.bind
  • JSON.stringify lite

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)

  1. Debounce with cancel + flush
  2. Promise.all
  3. Emitter with once
  4. LRU cache
  5. Deep equality
  6. Flatten with depth param
  7. Throttle trailing
  8. 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

Further reading

Patterns transfer. When you see a new prompt, name the pattern first — the implementation follows.