Theory

Search-as-you-type, and debounce

We have a gallery of pictures. Let's add search-as-you-type: the user types, and the grid immediately narrows to the matching pictures. Since we already downloaded the data, we filter client-side — no new requests to the server.

The filter is simple — case-insensitive matching on the title:

pictures.filter(p => p.title.toLowerCase().includes(query.toLowerCase()));

But there's a trap: when to filter. The naive way is on every keystroke:

searchInput.addEventListener('input', (e) => { filterAndRender(e.target.value); });

Typing "andromeda" fires that 9 times — re-rendering the whole grid each time. For a local search that's at least janky; and if the search went to the server (as it does in real projects), that would be 9 requests instead of one, per keystroke.

The fix — debounce. The idea: do nothing until the user stops typing. Technically — setTimeout and clearTimeout:

function debounce(fn, delay = 250) {
  let timer;
  return (...args) => {
    clearTimeout(timer);                 // cancel the previously scheduled run
    timer = setTimeout(() => fn(...args), delay);   // schedule a new one after delay
  };
}

Each keystroke cancels the previous pending run and schedules a new one. Only the last one fires — when nothing's been pressed for 250 ms. "andromeda" → one filter, not nine.