Back to Performance
Performance
medium
mid

How would you use throttling, debouncing, or other optimizations to prevent excessive requests?

Debounce for 'wait until the user stops' (search-as-you-type); throttle for 'at most once per interval' (scroll, resize). Plus: cancel stale in-flight requests (AbortController), cache/dedupe (React Query), batch, and paginate. Pick the technique by the event's nature.

5 min read·~8 min to think through

"Excessive requests" has several causes; the fix depends on which. Know the toolbox and when each applies.

Debounce vs throttle — the core distinction

  • Debounce — wait until activity stops for N ms, then fire once. For events where only the final value matters: search-as-you-type, autosave, resize-then-recalculate.
js
const debouncedSearch = debounce((q) => fetchResults(q), 300);
  • Throttle — fire at most once per N ms during continuous activity. For events where you want periodic updates: scroll position, mousemove, infinite-scroll triggers, drag.

Mnemonic: debounce = "wait for the pause," throttle = "rate-limit a stream."

The rest of the toolbox

Cancel stale requests. Debouncing reduces how often you fire, but in-flight requests can still resolve out of order. Use AbortController to cancel the previous request when a new one starts — prevents the race where an old response overwrites a newer one.

js
const controller = new AbortController();
fetch(url, { signal: controller.signal });
// on next input: controller.abort();

Cache & dedupe. A library like React Query / SWR caches by key, dedupes identical concurrent requests, and serves cached data instantly — often the biggest win. Don't refetch what you already have.

Batch. Combine many small requests into one (e.g. fetch 50 ids in a single call instead of 50 calls). DataLoader-style batching.

Paginate / virtualize. Don't request 10k rows — request a page at a time, load more on demand.

Guard at the source. Disable the submit button while a request is pending; { leading: true } debounce for immediate first response.

Putting it together — search-as-you-type

Debounce the input (300ms) → on fire, abort the previous request → React Query caches results by query string → minimum length guard (don't search on 1 char). That's debounce + cancellation + cache + guard, layered.

The framing

"First I pick the right tool for the event: debounce when only the final value matters — search input, autosave — throttle when I want periodic updates from a continuous stream — scroll, resize. But debouncing alone isn't enough: I cancel stale in-flight requests with AbortController to kill out-of-order races, cache and dedupe with React Query so I don't refetch known data, and batch or paginate to cut request count structurally. They layer — a real search box uses all of them."

Follow-up questions

  • When do you debounce vs throttle?
  • Why isn't debouncing enough — what does AbortController add?
  • How does React Query reduce request count?
  • What's the difference between leading and trailing debounce?

Common mistakes

  • Using debounce where throttle is needed (or vice versa).
  • Debouncing but not cancelling in-flight requests — out-of-order responses.
  • Re-creating the debounced function every render so it never debounces.
  • Not caching, so the same query refetches repeatedly.

Performance considerations

  • Debounce/throttle cut event-handler and request frequency; cancellation prevents wasted work and race bugs; caching eliminates redundant network entirely. Together they reduce both network load and re-render churn.

Edge cases

  • An older response resolving after a newer one (race condition).
  • Debounced function losing its timer on re-render (wrap in useMemo/useRef).
  • User submits via Enter before the debounce fires.
  • Component unmounts with a pending debounced call — cancel on cleanup.

Real-world examples

  • Search-as-you-type: debounced input + AbortController + React Query cache.
  • Throttled scroll handlers for infinite scroll and sticky headers.

Senior engineer discussion

Seniors pick debounce vs throttle by the event's nature, then go beyond it: AbortController for stale-request races, query-library caching/deduping, batching, and pagination — treating 'excessive requests' as a layered problem, not a single debounce call.

Related questions