Theme Framework How the LeaseLeads theme frontend works

Limiters

Debouncer, Throttler, and Sleep — the rate-limiting and delay primitives, all triggered through run().

On this page 4

Three small classes under utilities/Limiters/ cover the recurring problem of doing something less often, or later. Debouncer and Throttler both share one convention worth committing to memory: you trigger them with run(), not by calling the instance.

Debouncer

Collapses a burst of triggers into a single invocation. Construct it with a callback and delay, plus three booleans that pick which edges fire, then call run() on every event:

js
new Debouncer(callback, delay = 100, leading = false, trailing = true, required = false)
Option Default Fires…
leading false immediately on the first trigger of a burst
trailing true once the burst has been quiet for delay
required false on any trigger where delay has already elapsed since the last run

On each run() it applies leading first, then the required check, then (re)arms the trailing timer — so a steady stream of triggers collapses to the edges you asked for.

When you just want a debounced function rather than an instance to hold, the static Debouncer.debounce(...) builds one and hands back its bound run:

js
// components/Masonry/Masonry.js — leading + trailing, 50ms
this.#_refresher = Debouncer.debounce(
  () => this.#_refresh.call(this),
  50,
  true,
  true
)

// call the returned function on every event; it debounces internally
this.#_refresher()

Throttler

Guarantees a minimum interval between runs — the callback fires at most once per delay, and intervening triggers are dropped (no trailing catch-up). Same trigger convention: run() on every event, and the same static shortcut for a bound function:

js
const onScroll = Throttler.throttle(update, 100)
window.addEventListener('scroll', onScroll)
Note

Debounce vs throttle. Debounce waits for the storm to pass and fires once at the end (trailing) and/or the start (leading). Throttle fires on a steady cadence throughout. Reach for throttle on a continuous stream you want to sample (scroll), debounce on a burst you want to settle (resize, input).

Sleep

A cancellable, awaitable delay. Sleep.for(ms) returns a thenable you can await; calling .stop() resolves it immediately and cancels the pending timer. That makes it a restartable timer, not just a setTimeout wrapper:

js
// modules/Gallery/CarouselGallery.js — a self-cancelling filter delay
this.#_filterSleep.stop()          // cancel any in-flight wait
this.#_filterSleep = Sleep.for(250) // start a fresh one
// …elsewhere:
await this.#_filterSleep           // resolves after 250ms, or the moment .stop() runs

Because it's thenable, it drops straight into async flow with no wrapping promise — await Sleep.for(250) is all you write.

Tip

Seed a field with Sleep.for(0) (already-settled) so the first .stop() and reassignment have something to act on — the pattern the Gallery and VirtualTours carousels both use.

  • Global observers — Resize consumers commonly debounce their reactions.
  • Scheduling — the other half of the theme's timing toolkit.