Tem um bug de experiência que aparece em muito projeto: o usuário digita em um campo de busca e, a cada tecla, a aplicação dispara uma nova requisição.
Digitou “r”? API.
Digitou “re”? API.
Digitou “rea”? API.
Digitou “react”? Parabéns, você acabou de fazer cinco chamadas para descobrir uma palavra.
É aqui que entra o debounce.
O problema
Em interfaces com busca, autocomplete, filtros ou validações em tempo real, eventos podem disparar muitas vezes em poucos segundos. Isso pode gerar chamadas desnecessárias para API, processamento repetido e uma experiência menos fluida.
O problema não é o usuário digitar rápido. O problema é a aplicação reagir como se cada tecla fosse uma decisão final.
O que é debounce?
Debounce é uma técnica simples: em vez de executar uma função imediatamente, você espera um pequeno intervalo. Se a função for chamada de novo antes desse tempo terminar, o contador reinicia.
Na prática, isso permite esperar o usuário “parar de digitar” antes de buscar alguma coisa.
Debounce não é sobre deixar tudo mais lento. É sobre evitar trabalho inútil.
O snippet
function debounce(callback, delay = 500) {
let timeoutId;
return function (...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
callback.apply(this, args);
}, delay);
};
}
Como esse código funciona?
A função debounce recebe um callback e um tempo de espera em milissegundos. Ela retorna uma nova função que controla quando o callback original será executado.
Sempre que essa nova função é chamada, o timeout anterior é cancelado com clearTimeout. Em seguida, um novo timeout é criado com setTimeout.
Se nenhuma nova chamada acontecer dentro do intervalo definido, o callback finalmente roda.
Exemplo prático com campo de busca
<input id="search" type="search" placeholder="Buscar posts..." />
const input = document.querySelector("#search");
async function searchPosts(query) {
if (!query.trim()) return;
const response = await fetch(`/api/posts?q=${encodeURIComponent(query)}`);
const posts = await response.json();
console.log(posts);
}
const debouncedSearch = debounce((event) => {
searchPosts(event.target.value);
}, 600);
input.addEventListener("input", debouncedSearch);
Nesse exemplo, a função searchPosts só roda 600ms depois da última digitação. Se o usuário continuar digitando, o timeout anterior é cancelado e um novo começa.
Quando usar debounce?
Debounce é especialmente útil em situações como:
- campos de busca;
- autocomplete;
- filtros de produto;
- validação de formulário;
- eventos de resize da janela;
- scroll com cálculos pesados;
- requisições para API disparadas por interação do usuário.
Por que isso melhora a experiência?
Se cada tecla chama uma API, você está pedindo para o backend acompanhar o ritmo do teclado do usuário. Com debounce, você troca ansiedade por intenção.
A aplicação espera um pouquinho, entende melhor o que a pessoa quer e só então faz a chamada.
Às vezes, performance começa com uma função de dez linhas.
