Skip to content

throttle

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

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

Как и у всех операторов времени: стор даёт стор, событие даёт событие.

Скролл и перетаскивание

Классика: позиции сыплются каждый кадр, а получателю хватит и десяти раз в секунду:

ts
const scrolled = event<number>();
const position = throttle(scrolled, 100); // Event<number>

reaction({ on: position, run: (y) => { readingProgress.value = y / totalHeight.value; } });

Когда скролл останавливается, конечная позиция всё равно доходит — последнее значение окна выходит в его конце, а не теряется. Слайдер, превью перетаскивания, resize — та же схема.

Живые метрики без потопа

Стор обновляется бешено (тикер, прогресс загрузки), а интерфейсу нужно дышать:

ts
const progress = store(0);
const shown = throttle(progress, 250); // Store<number> — спокойный, можно рендерить

Мгновенный первый отклик

С leading: true значение выходит ещё и в момент открытия окна — первое сразу, последнее по-прежнему гарантировано:

ts
const tracked = throttle(mouseMoved, { ms: 100, leading: true });

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

  • Частые потоки, где получателю нужен темп, а не каждое значение: скролл, перетаскивание, resize, прогресс, курсоры.
  • Сигналы «пользователь активен» на сервер — не чаще раза в интервал.

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

Поведение

  • После каждого выхода значения окно начинается заново — два срабатывания никогда не ближе ms друг к другу, с leading или без.
  • Окна у каждого скоупа свои; dispose владельца отменяет незакрытые.
  • await вызова источника не ждёт значения, отложенного на конец окна: оно придёт отдельным обновлением, и его ошибки попадут в отчёт, а не в давно завершившийся await.