throttle
Пропускает не больше одного значения за окно — и последнее значение окна обязательно выйдет в его конце, так что ничего не пропадает.
throttle(source, ms);
throttle(source, { ms, leading: true });Как и у всех операторов времени: стор даёт стор, событие даёт событие.
Скролл и перетаскивание
Классика: позиции сыплются каждый кадр, а получателю хватит и десяти раз в секунду:
const scrolled = event<number>();
const position = throttle(scrolled, 100); // Event<number>
reaction({ on: position, run: (y) => { readingProgress.value = y / totalHeight.value; } });Когда скролл останавливается, конечная позиция всё равно доходит — последнее значение окна выходит в его конце, а не теряется. Слайдер, превью перетаскивания, resize — та же схема.
Живые метрики без потопа
Стор обновляется бешено (тикер, прогресс загрузки), а интерфейсу нужно дышать:
const progress = store(0);
const shown = throttle(progress, 250); // Store<number> — спокойный, можно рендеритьМгновенный первый отклик
С leading: true значение выходит ещё и в момент открытия окна — первое сразу, последнее по-прежнему гарантировано:
const tracked = throttle(mouseMoved, { ms: 100, leading: true });Когда пригодится
- Частые потоки, где получателю нужен темп, а не каждое значение: скролл, перетаскивание, resize, прогресс, курсоры.
- Сигналы «пользователь активен» на сервер — не чаще раза в интервал.
Когда не подходит: «дождаться, пока уляжется» (поисковый ввод) — это debounce. Throttle продолжает отдавать значения во время активности, debounce молчит до её конца.
Поведение
- После каждого выхода значения окно начинается заново — два срабатывания никогда не ближе
msдруг к другу, сleadingили без. - Окна у каждого скоупа свои; dispose владельца отменяет незакрытые.
awaitвызова источника не ждёт значения, отложенного на конец окна: оно придёт отдельным обновлением, и его ошибки попадут в отчёт, а не в давно завершившийсяawait.