Análise do uso de Debounce em Casos Assíncronos

Natalia Mattos
ProFUSION Engineering
5 min readOct 2, 2024

Descubra como e quando usar debounce assíncrono para otimizar suas funções.

Introdução

Debounce é uma técnica usada para controlar a frequência com que uma função é executada em resposta a eventos, especialmente em cenários nos quais o código é chamado repetidamente. Ele é frequentemente utilizado em JavaScript devido à interação com inputs de usuários e à sua implementação na maior parte da web. No entanto, debounce também pode ser aplicado em outros contextos e linguagens de programação. Ele ajuda a limitar o número de vezes que operações são executadas dentro de um intervalo de tempo. Embora funcione bem para casos síncronos, ao lidar com métodos assíncronos a escolha da implementação correta de debounce pode se tornar desafiador quando se tem por objetivo evitar comportamentos indesejados.

Neste post, exploraremos as diferenças entre bibliotecas de debounce síncrono e assíncrono, e por que é importante escolher a abordagem correta.

Debounce Síncrono vs. Assíncrono

Debounce Síncrono

O debounce síncrono atrasa a execução de uma função até que um período específico passe desde a última invocação. Isso significa que, se a função for chamada várias vezes dentro desse intervalo, apenas a última será executada, ajudando a evitar duplicidades e chamadas desnecessárias. Essa abordagem funciona bem para casos síncronos, nos quais o resultado está imediatamente disponível.

Uma das opções disponíveis no debounce é o immediate, que define se a função deve ser executada no início do período de debounce. No exemplo abaixo, o parâmetro immediate é passado como true:

Exemplo de Implementação de Debounce Síncrono com a biblioteca debounce:

import debounce from 'debounce';
const debouncedFunction = debounce(
(args) => genericFunction(args),
DEBOUNCE_WAIT_TIME_MS,
true,
);

Neste exemplo, a função genericFunction sofrerá o “debounce” com o tempo de espera especificado (DEBOUNCE_WAIT_TIME_MS), garantindo que nesse intervalo de tempo novas chamadas da função não sejam executadas, e que apenas a primeira chamada seja executada. O resultado da função é retornado imediatamente.

Opção Leading

Uma das opções disponíveis em implementações de debounce é o leading, também pode receber o nome de immediate dependendo da biblioteca como citado anteriormente, que define se a função deve ser chamada no início do período de debounce. Quando habilitado, a função é executada na primeira invocação, sendo ignorada até que o período de debounce expire. Embora útil em certos cenários, o uso de leading pode causar problemas em funções assíncronas se não for tratado adequadamente.

Debounce Assíncrono

Já o debounce assíncrono é projetado para lidar com funções que retornam promessas, como chamadas de API. Nesse caso, é necessário que o debounce aguarde a resolução da promessa antes de decidir se deve executar a função novamente. Isso garante que todas as operações assíncronas sejam concluídas antes de qualquer nova chamada. Um debounce simples não é eficaz nesses cenários.

Usar uma implementação inadequada pode causar problemas, como a função ser chamada antes que operações anteriores tenham sido concluídas, levando a comportamentos inesperados e inconsistências nos dados.

Por exemplo, ao usar debounce síncrono com funções assíncronas, os resultados podem ser incorretos: valores vazios ou resultados de chamadas anteriores. Também é possível que uma nova chamada seja iniciada antes da conclusão da anterior, comprometendo o propósito do debounce.

Escolhendo a Biblioteca Certa

Para implementar debounce em funções assíncronas, é essencial escolher uma biblioteca que suporte esses cenários.

  • Lodash
    O Lodash é uma das bibliotecas mais populares e oferece uma função debounce amplamente usada. No entanto, apesar de ser possível adaptá-la para lidar com operações assíncronas, ela não foi projetada com esse objetivo. Embora seja possível adaptar o comportamento para aguardar a resolução de promises, isso requer ativar a opção leading, o que pode interferir no funcionamento esperado em certos cenários. Além disso, ao usar TypeScript, adaptar o debounce do Lodash pode complicar a tipagem, tornando a implementação menos eficiente.
  • p-debounce
    Em contraste, o p-debounce, desenvolvido por sindresorhus (também responsável pela biblioteca debounce), foi criado especificamente para funções assíncronas. Ele garante que a resolução de uma função seja concluída antes de qualquer nova execução. Além disso, oferece a flexibilidade de ativar ou não o leading, proporcionando maior controle sobre quando a função será chamada, se adaptando melhor às necessidades do projeto.

Exemplo de Implementação de Debounce Assíncrono usando p-debounce:

import pDebounce from 'p-debounce';
const debouncedFunction = pDebounce(
(args) => genericFunction(args),
DEBOUNCE_WAIT_TIME_MS,
true,
);

Neste exemplo, a função sofrerá “debounce” corretamente, garantindo a conclusão da tarefa assíncrona antes de executar novas chamadas. O resultado é retornado apenas ao final da resolução da promessa da função.

Veja o exemplo a seguir, onde o debounce é utilizado em um cenário de barra de pesquisa: CodeSandbox. No trecho abaixo, o debounce controla a frequência com que a busca é realizada à medida que o usuário digita no campo de pesquisa:

const loadOptions = debounce(inputValue => {
return new Promise((resolve, reject) => {
if (inputValue.length < 2) return resolve([])
setTimeout(() => {
return resolve(filterStates(inputValue))
}, 1000)
})
}, 500)

A função loadOptions é utilizada para buscar ou filtrar dados com base no valor do inputValue, mas somente se o usuário digitar pelo menos 2 caracteres. Ela espera 500 ms (por causa do debounce) após o usuário parar de digitar para evitar execuções desnecessárias, e depois espera 1 segundo antes de retornar os resultados filtrados.

Essa abordagem é ideal para casos de autocomplete ou buscas dinâmicas em campos de input, evitando consultas muito frequentes ao servidor ou a um conjunto de dados.

Conclusão

Escolher a biblioteca de debounce certa é fundamental para garantir o comportamento adequado em suas aplicações. Enquanto o debounce síncrono funciona bem para funções imediatas, ao lidar com promessas é necessário utilizar um debounce assíncrono para assegurar que os resultados sejam consistentes. Com base nessa análise, o p-debounce se destaca como a melhor solução para funções assíncronas, oferecendo suporte nativo para promessas, flexibilidade no uso de leading e uma implementação simples.

Ao adotar o p-debounce, você garante que suas funções aguardem a conclusão completa de uma operação antes de permitir novas execuções, resultando em um código mais estável e confiável. Não esqueça de sempre consultar a documentação para melhores referências e adaptar o que é mais adequado para o seu projeto. Fique atento também às versões da biblioteca; atualmente, a mais recente com suporte ao CommonJS é a 2.1.0.

Referências

--

--

No responses yet