Artigo original: https://www.freecodecamp.org/news/javascript-debounce-example/

Em JavaScript, uma função de debounce garante que seu código seja disparado apenas uma vez por cada entrada do usuário. Sugestões da caixa de pesquisa, salvamentos automáticos de caixa de texto e eliminação de cliques duplos no botão são todos casos de uso para o debounce.

Neste tutorial, aprenderemos como criar uma função de debounce em JavaScript.

O que é o debounce?

O termo debounce vem da eletrônica. Ao apertar um botão, por exemplo, no seu controle remoto da TV, o sinal viaja pelo microchip do controle tão rapidamente que, antes de você conseguir tirar o dedo do botão, ele replica (bounces, em inglês) e o microchip registra seu clique diversas vezes.

debounce-button

Para mitigar isso, quando um sinal do botão é recebido, o microchip para de processar sinais do botão por alguns microssegundos, tempo durante o qual é fisicamente impossível apertá-lo novamente.

Debounce em JavaScript

Em JavaScript, o caso de uso é semelhante. Queremos acionar uma função, mas apenas uma vez a cada caso de uso.

Vamos dizer que nossa intenção é mostrar as sugestões de uma consulta de pesquisa, mas apenas após o visitante haver terminado de digitar.

Ou, então, digamos que nossa ideia é salvar as alterações em um formulário, mas somente quando o usuário não estiver trabalhando ativamente nas alterações, já que cada vez que salvamos os dados representa uma ida até o banco de dados.

Temos também o meu caso favorito — algumas pessoas ficarão tão acostumadas ao Windows 95 que clicam duas vezes em absolutamente tudo 😁.

Esta é uma implementação simples da função de debounce (CodePen aqui):

function debounce(func, timeout = 300){
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => { func.apply(this, args); }, timeout);
  };
}
function saveInput(){
  console.log('Salvando os dados');
}
const processChange = debounce(() => saveInput());

Ela pode ser usar em um elemento input:

<input type="text" onkeyup="processChange()" />

Ou em um elemento button:

<button onclick="processChange()">Clique aqui</button>

Ou mesmo em um evento de window:

window.addEventListener("scroll", processChange);

Ele também pode ser usado em outros elementos como uma função do JS simples.

Bem, o que está acontecendo aqui? A função debounce é especial, pois lida com duas tarefas:

  • Alocar um escopo para a variável timer
  • Agendar sua função para que seja acionada em um momento específico

Vamos explicar como isso funciona no primeiro caso de uso, com o elemento input de texto.

Quando um visitante escreve a primeira letra e libera a tecla, a função debounce primeiro reinicia o timer com clearTimeout(timer). Nesse ponto, o passo não é necessário, já que não há nada agendado ainda. Em seguida, ela agenda a função fornecida — saveInput() — para que seja invocada daqui a 300 ms.

Mas digamos que o visitante siga escrevendo, de modo que cada liberação de tecla acione novamente a função debounce. Cada invocação precisa reiniciar o timer. Em outras palavras, cancelar o planejamento inicial de saveInput() e reagendá-lo para daqui a um tempo — 300 ms depois. Isso segue acontecendo enquanto o visitante seguir apertando teclas com diferença de tempo inferior a 300 ms entre elas.

O último agendamento não será redefinido. É quando saveInput() será, finalmente, chamado.

E o contrário — como ignorar os eventos posteriores

O caso de uso anterior é bom para disparar eventos de salvamento automático ou de exibição de sugestões. Mas e quanto ao caso de uso em que se dá vários cliques em um único botão? Não queremos esperar o último clique, mas registrar o primeiro e ignorar o resto (CodePen aqui).

function debounce_leading(func, timeout = 300){
  let timer;
  return (...args) => {
    if (!timer) {
      func.apply(this, args);
    }
    clearTimeout(timer);
    timer = setTimeout(() => {
      timer = undefined;
    }, timeout);
  };
}

Aqui, acionamos a função saveInput() na primeira chamada de debounce_leading, causada pelo primeiro clique do botão. Agendamos a destruição do timer para 300 ms. Cada clique de botão subsequente dentro daquele intervalo de tempo já terá o timer e somente ativará a destruição do timer 300 ms depois.

Implementações do debounce em bibliotecas

Neste artigo, mostrei a você como implementar uma função de debounce em JavaScript e como usá-la para retardar eventos acionados pelos elementos de um site da web.

No entanto, você não precisa usar sua própria implementação de debounce em seus projetos se não quiser. Bibliotecas de JS amplamente usadas já contêm essa implementação. Aqui estão alguns exemplos:

BibliotecaExemplo
jQuery (via biblioteca)$.debounce(300, saveInput);
Lodash_.debounce(saveInput, 300);
Underscore_.debounce(saveInput, 300);