debounce
Ждёт паузу в потоке значений и один раз отдаёт последнее из них.
debounce(source, ms);
debounce(source, { ms, leading: true });Что на входе, то и на выходе: стор даёт стор (в нём лежит последнее устоявшееся значение, стартует он с начального значения источника), событие даёт событие.
Поиск по мере ввода
Главный сценарий. Из сырого стора ввода получаем стор, который меняется, только когда пользователь перестал печатать:
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 — обычный стор: от него можно строить производные, на него можно подписываться и читать его в скоупе.
Автосохранение черновика
То же самое с событием. Сохраняем, когда пользователь закончил править:
const draftChanged = event<Draft>();
const settled = debounce(draftChanged, 2000); // Event<Draft>
reaction({ on: settled, run: (draft) => void saveDraftFx(draft) });Каждая правка откладывает сохранение ещё на две секунды; на весь заход редактирования — один вызов saveDraftFx.
Сработать сразу, переждать остальное
leading: true меняет порядок: первое срабатывание проходит немедленно, дальше — тишина, пока не наберётся полная пауза:
const refresh = debounce(refreshClicked, { ms: 1000, leading: true });Первый клик обновляет сразу; лихорадочные повторные клики ничего не делают, пока пользователь не успокоится хотя бы на секунду.
Когда пригодится
- Ввод, за которым стоит что-то дорогое: поиск, проверка на сервере, фильтрация большого списка.
- Автосохранение, отметки «прочитано» — всё, что должно сработать один раз на всплеск активности и с последним значением.
- С
leading— кнопки, которые должны отвечать мгновенно, но не по десять раз.
Когда не подходит: нужен ровный темп прямо во время активности (прогресс при скролле) — это throttle; нужно просто «чуть позже» — это delay.
Поведение
- Окно тишины у каждого скоупа своё. Набор текста в одном скоупе не задевает другой — у теста и SSR-запроса свои паузы.
awaitвызова источника не ждёт отложенного срабатывания. Оно придёт отдельным обновлением черезms; если ниже по цепочке что-то упадёт, ошибка попадёт в отчёт, а не станет unhandled rejection.- Dispose владельца отменяет невыстреливший таймер — после разбора ничего не сработает.
- Фейк-таймеры работают без настройки: внутри обычный
setTimeout, так чтоvi.useFakeTimers()иvi.advanceTimersByTime(300)двигают оператор как надо.