Skip to content

debounce

Ждёт паузу в потоке значений и один раз отдаёт последнее из них.

ts
debounce(source, ms);
debounce(source, { ms, leading: true });

Что на входе, то и на выходе: стор даёт стор (в нём лежит последнее устоявшееся значение, стартует он с начального значения источника), событие даёт событие.

Поиск по мере ввода

Главный сценарий. Из сырого стора ввода получаем стор, который меняется, только когда пользователь перестал печатать:

ts
import { computed, store } from "@virentia/core";
import { debounce } from "@virentia/core/utils";

const query = store("");
const settled = debounce(query, 300); // Store<string>

const results = computed(() => search(list.value, settled.value));

results пересчитывается один раз на паузу, а не на каждую букву — и всё, что на него подписано, обновляется так же редко. При этом settled — обычный стор: от него можно строить производные, на него можно подписываться и читать его в скоупе.

Автосохранение черновика

То же самое с событием. Сохраняем, когда пользователь закончил править:

ts
const draftChanged = event<Draft>();
const settled = debounce(draftChanged, 2000); // Event<Draft>

reaction({ on: settled, run: (draft) => void saveDraftFx(draft) });

Каждая правка откладывает сохранение ещё на две секунды; на весь заход редактирования — один вызов saveDraftFx.

Сработать сразу, переждать остальное

leading: true меняет порядок: первое срабатывание проходит немедленно, дальше — тишина, пока не наберётся полная пауза:

ts
const refresh = debounce(refreshClicked, { ms: 1000, leading: true });

Первый клик обновляет сразу; лихорадочные повторные клики ничего не делают, пока пользователь не успокоится хотя бы на секунду.

Когда пригодится

  • Ввод, за которым стоит что-то дорогое: поиск, проверка на сервере, фильтрация большого списка.
  • Автосохранение, отметки «прочитано» — всё, что должно сработать один раз на всплеск активности и с последним значением.
  • С leading — кнопки, которые должны отвечать мгновенно, но не по десять раз.

Когда не подходит: нужен ровный темп прямо во время активности (прогресс при скролле) — это throttle; нужно просто «чуть позже» — это delay.

Поведение

  • Окно тишины у каждого скоупа своё. Набор текста в одном скоупе не задевает другой — у теста и SSR-запроса свои паузы.
  • await вызова источника не ждёт отложенного срабатывания. Оно придёт отдельным обновлением через ms; если ниже по цепочке что-то упадёт, ошибка попадёт в отчёт, а не станет unhandled rejection.
  • Dispose владельца отменяет невыстреливший таймер — после разбора ничего не сработает.
  • Фейк-таймеры работают без настройки: внутри обычный setTimeout, так что vi.useFakeTimers() и vi.advanceTimersByTime(300) двигают оператор как надо.