Nó 26 Debounce e Throttle: Guia de utilitários vs Lodash


Node.js 26.10.0 adicionado util.debounce e util.throttle para node:utilpara que você possa eliminar um manipulador de observação de arquivos ou limitar chamadas de API de saída sem Lodash. Eles são baseados em promessas e se comportam de maneira diferente do Lodash em alguns aspectos que são importantes quando você migra. Este guia mostra ambas as APIs com exemplos executados no Node 26.10.0, depois as compara com o Lodash 4.17 e lista as dicas.

Avaliado em 4 de outubro de 2026 contra o funcionário nó: documentação do utilitário e o Registro de alterações do Node.js 26e todos os exemplos foram executados em Node.js 26.10.0.

Verifique se sua versão do Node os possui

Ambas as funções foram adicionadas na v26.10.0. Em versões mais antigas, util.debounce é undefined. Verifique antes de confiar neles:

node --version
node --input-type=module -e "import { debounce, throttle } from 'node:util'; console.log(typeof debounce, typeof throttle);"

Em 26.10.0 ou posterior isso é impresso function function. Os exemplos abaixo usam a sintaxe do módulo ES, então salve-os como .mjs arquivos ou conjunto "type": "module" em seu package.json.

Debounce vs acelerador em um minuto

O Debounce espera por um período de silêncio e depois é executado uma vez, com os argumentos mais recentes. Use-o quando quiser agir após o término de uma explosão, como um salvamento de arquivo que dispara vários eventos do inspetor. O acelerador limita a frequência com que o trabalho é iniciado. Use-o quando os eventos continuarem chegando e você desejar uma taxa constante, como chamadas para uma API com taxa limitada.

utilitário.debounce

A assinatura é util.debounce(fn, wait(, options)). As opções são leading (executar no início de uma janela, padrão false), rejectOnCancel (rejeitar as promessas de chamadas substituídas, padrão false) e signal (um AbortSignal que cancela chamadas pendentes).

A função debounce retorna uma promessa. Por padrão, cada chamada na mesma janela é resolvida com o resultado da única invocação que realmente foi executada:

import { debounce } from 'node:util';

const save = debounce((value) => {
  console.log('saved', value);
  return value;
}, 100);

const results = await Promise.all((save(1), save(2), save(3)));
console.log(results);

A função retornada também tem cancel(), flush(), pending, pendingCount, ref() e unref(). Chamando unref() permite que o processo saia enquanto um temporizador de rejeição está pendente.

Exemplo: reconstruir uma vez após uma explosão de eventos de arquivo

fs.watch frequentemente relata vários eventos para um salvamento. A eliminação do manipulador executa a reconstrução uma vez, 300 ms após o último evento:

import { debounce } from 'node:util';
import { watch } from 'node:fs';

const rebuild = debounce((file) => {
  console.log(`Rebuilding after change to ${file}`);
}, 300);

function ignoreAbort(err) {
  if (err.name !== 'AbortError') throw err;
}

const watcher = watch('./src', { recursive: true }, (_event, file) => {
  rebuild(file).catch(ignoreAbort);
});

process.on('SIGINT', () => {
  rebuild.cancel();
  watcher.close();
});

Três gravações rápidas em um arquivo em ./src produziu uma linha “Reconstruindo” em uma execução de teste no Nó 26.10.0.

A pegadinha cancel()

cancel() rejeita todas as promessas pendentes com um AbortError. Se você ignorar a promessa retornada, o que é natural em um manipulador de eventos, essa rejeição se tornará uma rejeição não tratada e o Node sairá com um erro. Em um teste, ligando cancel() em uma função rejeitada cuja promessa ninguém administrou travou o processo. É por isso que o exemplo acima anexa .catch(ignoreAbort) para cada chamada. Lide com a promessa ou use um AbortSignal e lidar com as rejeições da mesma maneira.

utilitário.throttle

A assinatura é util.throttle(fn, limit, interval(, options)). Permite no máximo limit invocações de fn para começar em cada interval milissegundos. Isso difere do Lodash, onde você passa um único tempo de espera. As opções são:

  • overflow: 'queue' (padrão) mantém chamadas extras em ordem, 'drop' os rejeita imediatamente.
  • maxPending: o maior tempo que a fila pode crescer quando overflow é 'queue'.
  • concurrency: quantas invocações podem ser resolvidas de uma vez.
  • strict: quando truenão mais do que limit as chamadas começam em qualquer intervalo contínuo, não apenas em janelas fixas.
  • signal: um AbortSignal que cancela chamadas pendentes.

Exemplo: chame uma API no máximo duas vezes por segundo

import { throttle } from 'node:util';

const started = Date.now();
const callApi = throttle(async (id) => {
  console.log(`request ${id} started at ${Date.now() - started} ms`);
  return { id };
}, 2, 1_000);

const results = await Promise.all((1, 2, 3, 4, 5).map((id) => callApi(id)));
console.log(results.map((r) => r.id));

No Nó 26.10.0, isso imprimiu as solicitações 1 e 2 em cerca de 1 ms, as solicitações 3 e 4 em cerca de 1.001 ms e a solicitação 5 em cerca de 2.003 ms. Cada chamada foi executada em ordem, cada uma com seus próprios argumentos. Uma chamada limitada é colocada na fila e não descartada.

Exemplo: descarte chamadas em excesso em vez de colocá-las na fila

import { throttle } from 'node:util';

const notify = throttle((msg) => msg, 1, 1_000, { overflow: 'drop' });

console.log(await notify('first')); 
try {
  await notify('second');
} catch (err) {
  console.log(err.code); 
}

Uma chamada perdida retorna uma promessa rejeitada com um ERR_THROTTLED erro. O nó marca essa promessa como tratada, portanto, ignorá-la não gera um unhandledRejection. Você também pode ligar hasImmediateCapacity() para verificar se uma chamada seria executada imediatamente.

Mudando de Lodash

As funções do Node são semelhantes a _.debounce e _.throttlemas eles não são substitutos imediatos. Verifique essas diferenças em relação ao Documentação do Lodash 4.17:

  • Valor de retorno. Lodash retorna o resultado da última invocação. O nó retorna uma promessa, então os chamadores precisam await ou .then.
  • Forma do acelerador. _.throttle(fn, wait) é executado no máximo uma vez por wait e usa os argumentos mais recentes. util.throttle(fn, limit, interval) enfileira cada chamada com seus próprios argumentos, a menos que você defina overflow: 'drop'.
  • maxEspere. Lodash _.debounce tem maxWait. As opções de Node listadas acima não têm equivalente.
  • rastreando. Lodash permite desligar a borda de fuga. As opções de debounce do Node são leading, rejectOnCancel e signal.
  • lave e cancele. Ambos têm flush() e cancel(). Nó cancel() rejeita promessas pendentes.

Se você precisar apenas de um simples debounce baseado em espera e lidar com a promessa retornada, a troca é uma pequena mudança. Se você confiar maxWaitno acelerador drop-the-extras do Lodash, ou você compartilha código com o navegador, mantenha Lodash.

PrecisarUsar
Execute uma vez após uma série de eventos no Node 26.10+util.debounce
Inicie no máximo N chamadas por intervalo e mantenha todas as chamadasutil.throttle (fila)
Ignore chamadas em excesso em vez de ficar na filautil.throttle com overflow: 'drop'
maxWaitcontrole de borda final ou suporte ao navegadorLodash
Proteja um endpoint HTTP contra tráfego externoUm limitador de taxa em middleware ou gateway

Armadilhas a evitar

  • AbortError não tratado. Lide com promessas de funções rejeitadas antes de ligar cancel()conforme mostrado acima.
  • Desvio de versão. O código que usa essas funções falha nas versões do Node anteriores a 26.10.0. Fixe o tempo de execução em engines ou IC.
  • Não é um limitador de taxa para solicitações recebidas. Envolvendo um manipulador de rota em util.throttle controla a frequência com que a função interna é executada. Isso não impede a chegada de solicitações.
  • Crescimento da fila. A fila padrão é ilimitada. Definir maxPending ou usar overflow: 'drop' quando os chamadores podem ultrapassar o limite.

Principais conclusões

  • util.debounce(fn, wait, options) e util.throttle(fn, limit, interval, options) enviado em Node.js 26.10.0.
  • Ambos retornam promessas e apoio AbortSignal cancelamento.
  • Acelere as filas por padrão, o que difere do Lodash.
  • Sempre lide com a promessa ao usar cancel().

Fontes



Source link