Debouncing AI Calls
Wait for the user to stop typing before calling the AI, so you fire one request instead of dozens.
TL;DR
- Wait until typing pauses before firing the
request. - Skip empty, short, or unchanged
inputvalues. - Combine debounce with
cancellationto drop stale calls.
Debounce the Input
Set a TimerWait a beat after the last keystroke.
const id = setTimeout(() =>
setDebounced(value), 400);Clear on ChangeCancel the pending timer each keystroke.
return () => clearTimeout(id);Use the Delayed ValueAct on the settled value, not the raw one.
useEffect(() => { ask(debounced); },
[debounced]);A useDebounce Hook
Reusable HookWrap the pattern for any value.
function useDebounce(v, ms = 400) {
const [d, setD] = useState(v);Effect and TimerUpdate the value after the delay passes.
useEffect(() => {
const id = setTimeout(() => setD(v), ms);
return () => clearTimeout(id);
}, [v, ms]);Return the ValueGive back the debounced value.
return d;Skip Wasteful Calls
Minimum LengthIgnore inputs too short to be useful.
if (query.trim().length < 3) return;Skip UnchangedDo not re-ask the same question.
if (query === lastRef.current) return;Guard EmptyNever call the AI with a blank prompt.
if (!query.trim()) return;Trigger the Call
Fire on SettleCall the AI when the debounced value changes.
useEffect(() => { run(debounced); },
[debounced]);Pair With AbortCancel any earlier request first.
controllerRef.current?.abort();One RequestA whole sentence becomes a single call.
// dozens of keystrokes -> 1 requestTips
- Debounce around 300 to 500 milliseconds for chat inputs; short enough to feel instant, long enough to skip mid-word
keystrokes. - Store the debounced value in state with a
useEffecttimeout, then trigger the AI call from that value, not the raw input.
Warnings
- Do not call the AI on every
onChange; a fast typist can fire dozens of paid requests for a single question. - Clear the previous
timeouton each keystroke, or overlapping timers will fire several requests at once.
In Practice
A search box that asks the AI once after the user stops typing, not on every key.
- useDebounce delays the query until typing pauses for 400 ms.
- The effect runs only when the debounced value changes.
- A length guard skips tiny inputs that would waste a request.
- One settled query triggers exactly one AI call.
function Search() {
const [q, setQ] = useState('');
const debounced = useDebounce(q, 400);
const [answer, setAnswer] = useState('');
useEffect(() => {
if (debounced.trim().length < 3) return;
fetch('/api/chat', {
method: 'POST',
body: JSON.stringify({ q: debounced }),
})
.then((r) => r.json())
.then((d) => setAnswer(d.text));
}, [debounced]);
return <input value={q}
onChange={(e) => setQ(e.target.value)} />;
}FAQ
AI calls are slow and cost money per request. Firing one on every keystroke sends dozens of requests for a single question and wastes tokens. Debouncing waits until the user pauses, so you send one request with the final input.
For chat or search inputs, 300 to 500 ms feels responsive while skipping mid-word typing. Shorter delays fire too often; longer ones feel laggy. Tune it to your input and test with a fast typist.
Debounce. Throttle fires at a steady rate during continuous input, which suits scroll or resize. For a user finishing a thought before you call the model, you want to wait for the pause, which is debounce.