Olá, Web Developers!
Hoje vou falar sobre Debounce e a importância de saber quando usar em nossas aplicações.
Se você nunca usou essa técnica ao lidar com eventos como scroll
, resize
, mousemove
, key*
, etc, pode ser que enfrente problemas com seus dados e com a performance da sua aplicação.
Curso JavaScript Básico
Conhecer o cursoUm exemplo básico
Imagine que temos um simples input
do tipo texto. Nós vamos querer que, conforme o usuário vá escrevendo, alguns valores sejam exibidos como sugestão, assim como quando iniciamos uma busca no Google.
Então, quando o usuário para de escrever, temos que enviar o que ele escreveu para o nosso servidor para poder exibir as sugestões.
No exemplo abaixo, ao invés de fazer uma requisição, eu apenas incrementei um contador para vermos quantas requisições teríamos feito se essa fosse uma aplicação de verdade.
const myInput = document.querySelector('input'),
mySpan = document.querySelector('span');
let counter = 0;
myInput.addEventListener('input', function(){
mySpan.innerText = ++counter;
})
Vamos usar o evento “onInput” como exemplo. Se escrevermos “TreinaWeb” nesse input, veja que o contador vai acusar que o evento foi disparado 9 vezes.
Imagine só: apenas para enviar “TreinaWeb” para nosso servidor, teríamos feito 9 requisições se tivéssemos usado um simples evento “onInput” sem nenhum tratamento.
Pode parecer pouco, mas pense quando precisamos escutar outros eventos, como um scroll
ou mousemove
. Uma simples ação do usuário pode acabar disparando centenas ou até milhares de eventos.
Uma solução melhor é fazer uma verificação para saber se o usuário ainda está escrevendo. Se ele parou de digitar, então podemos enviar nosso texto para o servidor.
É aí que entra o Debounce
. Ele permite a execução de uma função apenas se um determinado tempo se passou.
Um tratamento simples
Teste agora o exemplo abaixo com o tratamento para ver a diferença. Ao escrever com uma certa velocidade, o evento só será disparado quando você encerrar a digitação.
Mudamos pouca coisa no código:
const myInput = document.querySelector('input'),
mySpan = document.querySelector('span');
let counter = 0,
timer = null; // variavel para armazenar nosso timer
myInput.addEventListener('input', function(){
// limpamos o timer
clearTimeout(timer);
// armazenamos o timer novamente
timer = setTimeout(function(){
mySpan.innerText = ++counter;
}, 500);
})
Veja só o que fizemos:
Nossa função agora é executada dentro de um setTimeout
. Estamos passando 500ms, ou seja, nosso código só será executado após meio segundo. Você pode mudar esse valor de acordo com sua necessidade.
Nós guardamos o resultado do setTimeout
numa variável que chamamos de timer
. Acontece que enquanto o usuário estiver digitando, menos de 500ms se passarão, então o nosso evento input
será executado novamente. Veja que logo no começo dele nós limpamos o nosso timer com a função clearTimeout
. Isso quer dizer que antes mesmo do nosso código ser executado, nós já cancelamos ele. E aí criamos outro setTimeout
.
Caso o usuário pare de digitar, nosso evento onInput
não será executado novamente, fazendo com que a linha onde executamos o clearTimeout()
não seja executada, permitindo que o nosso código finalmente seja executado.
Bem simples e faz um grande benefício para nossas aplicações. Algumas bibliotecas, como o RxJS, nos fornecem já a opção de debounce para executarmos funções numa quantidade reduzida caso haja a possibilidade delas serem chamadas muitas vezes em um tempo muito curto.
Criando uma função “debounce” para reutilização
Tudo bem que só adicionamos menos de cinco linhas em nosso código para tratar uma função, mas imagine que a gente esteja em um sistema e a gente queira tratar várias funções.
Para isso, é melhor criar uma função que trate o debounce para nós.
const myInput = document.querySelector('input'),
mySpan = document.querySelector('span');
let counter = 0;
function debounce(func, wait) {
let timer = null;
return function() {
clearTimeout(timer);
timer = setTimeout(func, wait);
}
}
myInput.addEventListener('input', debounce(function(){
mySpan.innerText = ++counter;
}, 500));
No código acima nós simplesmente criamos uma função debounce()
que já faz o que conhecemos: tem uma variável “timer” e executa uma função que limpa o timer e executa nossa função após um determinado tempo.
Para essa função debounce()
nós passamos a função que queremos executar e o tempo que ela deve esperar para ser executada.
Agora temos o mesmo comportamento e também temos a possibilidade de reutilizar essa função em outros lugares.