Entenda a diferença entre Throttle e Debounce (de forma visual e interativa)
Como programadores, eventualmente nos deparamos com uma situação em que um evento ocorre muitas vezes e acaba deixando o processamento desnecessariamente lento. Por exemplo, imagine uma situação onde você precisa criar um formulário com salvamento automático, então resolve salvar sempre que o usuário pressiona uma tecla; ou que você precise redesenhar tudo o que está na tela quando o usuário redimensionar a janela. Realizar essas ações sempre que os eventos acontecem pode ser bem custoso.
Então, você descobre duas coisas que podem te ajudar: debounce e throttle. Elas acabam sendo tão "parecidas" que existem vários artigos sobre a diferença entre as duas, e no começo é normal esquecer e precisar pesquisar "debounce vs throttle" para relembrar o que é cada coisa.
- Função comum: A função é chamada sempre que o evento (gatilho) ocorre.
- Debounce: Uma função "debounced" é chamada após um período de tempo N desde sua última chamada. Ou seja, se o período definido for 1 segundo, não importa quantos eventos ocorrerem, a função será chamada novamente apenas caso fique 1 segundo sem receber o evento.
- Throttle: O throttle serve para reduzir o número de vezes que uma ação pode ser chamada em um determinado período de tempo N. Se seguirmos o exemplo de um período de 1 segundo, isso significa que a função só será chamada novamente após pelo menos 1 segundo ter passado, caso o evento ocorra novamente.
Talvez eu não tenha conseguido explicar bem, mas a verdade é que é realmente difícil entender sem um exemplo prático, ou sem uma explicação visual ou interativa. É por isso que resolvi trazer um artigo para cá: Debounce vs Throttle: Definitive Visual Guide. A animação não funciona bem no Firefox, então se você estiver num navegador que parece "esquisito", experimente acessar o site pelo Chrome.
Função comum
O exemplo disponibilizado no site é uma máquina que libera uma bolinha sempre que um botão é pressionado. Quer dizer, este é o exemplo para uma "função comum". Veja abaixo e preste atenção (neste e nos próximos exemplos) na contagem "Button clicked" e "Event handler called":
Throttle
Agora, se o botão da máquina funcionar com throttle, como fica? Bom, vamos imaginar que o tempo do throttle seja de 500ms. Isso significa que, se você pressionar o botão a cada 300ms, uma bolinha será liberada a cada clique após os 500ms. Ou seja, a função só será executada novamente após, pelo menos, 500ms. Por exemplo:
- Clique 1 (0ms): Libera bolinha.
- Clique 2 (300ms): Não libera.
- Clique 3 (600ms): Libera bolinha.
- Clique 4 (900ms): Não libera.
Veja um exemplo visual pelo site, com cliques sem um intervalo bem definido entre eles:
Debounce
No caso do debounce, a máquina irá liberar a bolinha apenas após ficar N milésimos de segundo sem ser pressionada. Ou seja, se o tempo for 500ms e você pressionar a cada 300ms, não terá nenhuma bolinha até parar de pressionar. No exemplo do artigo, a função debounce não é executada inicialmente, diferentemente da throttle, então fica assim:
- Clique 1 (0ms): Não libera bolinha.
- Clique 2 (300ms): Não libera.
- Clique 3 (600ms): Não libera.
- Clique 4 (900ms): Não libera.
- (1400ms): Libera bolinha.
No exemplo acima, o clique 4 foi o gatilho que liberou a bolinha porque não teve outro clique durante os próximos 500ms (entre o tempo 900ms e o 1400ms), mas a bolinha surgiu apenas 500ms após o clique.
Veja agora um exemplo com a máquina do site, novamente com cliques sem um intervalo bem definido entre eles:
Agora uma curiosidade aplicada ao mundo real: se o interruptor de luz tivesse um debounce imbutido, nenhuma criança (ou adulto) conseguiria brincar de ficar acendendo e apagando a luz rápido 😅
Em algumas situações, pode ser útil mudar o comportamento do debounce para a função ser executada na primeira tentativa.
Espero que esse site sirva de referência rápida para vocês como serviu para mim, quando estava começando a trabalhar com frontend e precisei lidar com eventos gerados pelo usuário.