Debounce vs. Throttle no Javascript

React 27 de Dez de 2017

Em muitos casos em que controlamos ações realizadas pelo usuário no front-end da nossa aplicação precisamos manipular os intervalos entre as execuções das funções que são ativadas de acordo com cada comportamento.

Imagine um formulário de busca, gostaríamos que a cada letra que o usuário digita já buscar os resultados na API? Ou só depois de 500ms após parar de digitar? Ou ainda, realizar a busca na API a cada 300ms independente se o usuário parou de digitar ou não?

A verdade é que isso é muito comum, especialmente no Javascript, e daí surgiram os termos Debounce e Throttle. Para entendermos melhor como funciona cada um desses conceitos, vamos à prática.

Para testar as funcionalidades do Debounce e Throttle vou utilizar a biblioteca Lodash que já possui essas funções implementadas.

Debounce

O Debounce é provavelmente o termo mais comum e é utilizado em vários tipos de aplicações, mas por que? Imagine que estamos ouvindo o evento de redimensionamento de tela por parte do usuário, ou seja, toda vez que a tela aumentar ou diminuir de tamanho, ativamos uma função que recebe esses dados de largura e altura e imprime em tela. Sem a utilização do Debounce, se redimensionarmos nossa tela em alguns pixels, temos uma série de disparos na função, pois cada pixel redimensionado é responsável por um disparo ao evento:

function onResize(event) {
  var node = document.createElement("p");
  var textnode = document.createTextNode(event.target.outerWidth + "x" + event.target.outerHeight);
  node.appendChild(textnode);
  document.getElementById('content').appendChild(node);
}

window.onresize = onResize;

No exemplo acima, vemos que assim que redimensionamos a tela, o código imprime centenas de alterações em tela, agora imagine que essas alterações impliquem em códigos mais pesados que exigem processamento ou até em funções assíncronas, seria um total desastre.

Utilizando o Debounce, nós receberíamos o valor do tamanho da tela, apenas x milissegundos após o usuário parar de movê-la:

function onResize(event) {
  var node = document.createElement("p");
  var textnode = document.createTextNode(event.target.outerWidth + "x" + event.target.outerHeight);
  node.appendChild(textnode);
  document.getElementById('content').appendChild(node);
}

window.onresize = _.debounce(onResize, 500);

Com esse conceito, passamos a realizar menos chamadas à função onResize isso porque encapsulando-a no método _.debounce estamos controlando os intervalos de chamada da mesma, permitindo que seja chamada apenas se não houveram outras chamadas nos mesmos 500ms.

Ainda assim, essa técnica não resolve todos problemas, isso porque se precisarmos do valor do tamanho da tela mesmo quando o usuário ainda esteja a redimensionando não conseguiremos com o Debounce, aí temos o Throttle para nos ajudar.

Throttle

Com esse outro conceito, nós definimos um intervalo em que as chamadas devem acontecer, nesse caso, nós não temos apenas uma chamada quando o usuário pare de realizar a ação, mas sim várias chamadas ao decorrer que a ação é executada. Diferentemente da opção sem nenhum controle de intervalos, o Throttle vai permitir que a função onResize execute apenas uma vez a cada x milissegundos:

function onResize(event) {
  var node = document.createElement("p");
  var textnode = document.createTextNode(event.target.outerWidth + "x" + event.target.outerHeight);
  node.appendChild(textnode);
  document.getElementById('content').appendChild(node);
}

window.onresize = _.throttle(onResize, 500);

Agora dessa forma, obtemos o resultado do tamanho da tela impresso no corpo do código a cada 500ms, independente se o usuário parou ou não de redimensionar a janela.

Concluindo

Essas duas técnicas, apesar de muito simples, devem ser utilizadas com sabedoria no seu projeto, pense nos campos de busca, em ações que o usuário não deve fazer repetidamente ou até em funções que irão consumir muito processamento e por isso devem respeitar um intervalo.

E aí dev, gostou do post? Não esquece de deixar suas ?

Marcadores

Diego Fernandes

Programador full-stack, apaixonado pelas melhores tecnologias de desenvolvimento back-end, front-end e mobile, é co-fundador e CTO na Rocketseat.