Debounce function com javascript

Criando uma função de Debounce com javascript

Fala galera, tudo bem com vocês?
Hoje estarei demonstrando algumas formas de implementar uma função de debounce com javascript.

Pra que serve o Debounce?

Vamos dizer que você esta fazendo um input para o usuário digitar alguma coisa para pesquisar, por exemplo, produtos em uma loja, ou amigos em um rede social. Como você faria isso? Adicionaria um eventListener para identificar quando ele pressionasse uma tecla e faria sua busca no seu backend, e pronto tudo resolvido, certo? Errado! Pensa só, a cada tecla que o usuário pressionar vai ser disparada uma nova requisição para api fazendo a busca e isso é muito ruim, imagina que você tem vários usuário utilizando seu sistema, vão chegar muitas requisições ao mesmo tempo, e você vai perceber que são desnecessárias, porque veja só, se a pessoa for buscar a palavra blusa, ela vai fazer uma busca individual para o b, l, u, s e a. E ainda tem outro ponto, é uma palavra pequena seria muito mais interessante esperar um pouco para o usuário escrever mais, assim faria a busca ser bem mais assertiva.

Pra resolver esse tipo de problema foram surgindo diversas técnicas, uma delas é o Debounce.

Esse codepen simula esse problema que falei, cada vez que o usuário digita ou apaga uma letra vai estar executando a função que foi definida no eventListener, que nesse caso é a aumentarContador, que tem a função de ir aumentando o número de eventos que foram emitidos e exibir no contador.

https://codepen.io/allisonverdam/pen/oNbayYJ?editors=1011

Caso não queira abrir o codepen o código ficou assim:

<input id="input">
<br/>
<label for="input">Quantidade de eventos: <b>0</b></label>
const input = document.querySelector("input");
const counterElement = document.querySelector("b");

function aumentarContador() {
  counterElement.innerText = ++counterElement.innerText;
}

input.addEventListener("keydown", function () {
  aumentarContador();
});

Como resolvemos esse problema?

Eu vou estar mostrando algumas formas de implementar a função de debounce. Vou começar da que eu considero mais simples e vou avançando.

Vou demonstrar como seria a forma mais simples que conheço de implementar o debounce, mas já dando minha opinião, tem outras formas mais interessantes pra usar.

https://codepen.io/allisonverdam/pen/MWKPXBQ?editors=1010

const input = document.querySelector("input");
const counterElement = document.querySelector("b");

let timer;

function aumentarContador() {
  counterElement.innerText = ++counterElement.innerText;
}

input.addEventListener("keydown", function () {
  clearTimeout(timer);
  
  timer = setTimeout(function () {
    aumentarContador();
  }, 1000);
});

Vamos pra explicação.

Agora, toda vez que o evento keydown for acionado não executaremos diretamente a função aumentarContador, o que acontece agora é que adicionamos a função setTimeout para ter tempo de espera para executar a função. Mas por que isso resolveu o problema? não era pra somente estar demorando mais? Então, veja que além de adicionar esse tempo de espera colocamos um função chamada clearTimeout.
Vamos por parter para ficar mais claro.

  • Criamos uma variável chamada timer, ela é responsável por salvar o retorno do setTimeout
  • No primeiro momento a variável timer vai estar vazia, mas toda vez que o usuário pressiona uma tecla a variável timer irá receber o retorno da função setTimeout
  • Utilizamos a função clearTimeout para limpar a variável timer, isso fará com que toda vez que o usuário pressione uma tecla cancele o setTimeout anterior que estava salvo na variável timer.
  • Se o usuário ficar 1000ms sem digitar nada o clearTimeout não terá tempo de limpar a variável timer, fazendo com que execute a função aumentarContador que está dentro do setTimeout

Subindo o nível e deixando a função Debounce reutilizável

A forma que vou demostrar agora eu já acho muito legal, porque ela fica desacoplada, bem legível e simples.

https://codepen.io/allisonverdam/pen/OJMBEeW?editors=1010

const input = document.querySelector("input");
const counterElement = document.querySelector("b");

function aumentarContador() {
  counterElement.innerText = ++counterElement.innerText;
}

function debounce(func, wait = 1000, timer) {
  return function () {
    clearTimeout(timer);
    timer = setTimeout(func, wait);
  };
}

input.addEventListener("keydown", debounce(aumentarContador, 500));

A ideia do debounce em si continua a mesma, o que a gente fez foi criar uma função para essa lógica de debounce ser reutilizável.

Também adicionamos na assinatura da função debounce os parâmetros wait, que é responsável por informar o tempo que o setTimeout vai esperar para executar nossa função, que informada pelo parâmetro func e adicionamos também um parâmetro chamado timer, assim a gente não precisa criar uma variável do lado de fora, porque a função já vai criar automaticamente.

É isso pessoal, espero que esse conteúdo tenha sido útil pra você.