debouncekit
v0.1.1
Published
Tiny, type-safe debounce and throttle — lodash-compatible leading/trailing/maxWait plus AbortSignal and cancel/flush/pending. Zero dependencies.
Downloads
252
Maintainers
Readme
debouncekit
Tiny, type-safe
debounceandthrottle— lodash-compatibleleading/trailing/maxWait, plusAbortSignalandcancel/flush/pending. Zero dependencies.
You usually reach for lodash.debounce and pull in a chunk of lodash, or copy a
half-correct snippet that botches maxWait and leaks the timer. debouncekit is
the whole thing — correct leading/trailing/maxWait edges — in a zero-dependency,
fully-typed package, plus the modern bits lodash never added: an AbortSignal
to tear it down, and cancel / flush / pending for explicit control.
import { debounce } from "debouncekit";
const save = debounce((draft: string) => persist(draft), 300);
input.addEventListener("input", () => save(input.value)); // coalesces keystrokes
// later: save.flush() to persist now, save.cancel() to drop itWhy debouncekit?
- Debounce & throttle in one tiny module.
throttleis justdebouncewithmaxWait === wait, so behavior is consistent and predictable. - lodash-compatible edges.
leading,trailing, andmaxWaitbehave exactly as you expect — drop-in for the cases people actually use. - Cancellable. Pass an
AbortSignal; aborting cancels any pending call and disables further scheduling. No dangling timers. - Explicit control.
cancel()drops a pending call,flush()runs it now and returns its result,pending()tells you if one is scheduled. - Type-safe. Argument types, return type, and
thisare all preserved. - Zero dependencies, ESM + CJS + types, ~1 kB min+gzip.
Install
npm install debouncekit
# or: pnpm add debouncekit / yarn add debouncekit / bun add debouncekitdebounce(fn, wait?, options?)
Delays fn until wait ms have passed since the last call.
import { debounce } from "debouncekit";
const search = debounce((q: string) => fetchResults(q), 250);
search("a"); search("ab"); search("abc");
// only fetchResults("abc") runs, 250ms after the last keystrokeinterface DebounceOptions {
leading?: boolean; // fire on the leading edge (default false)
trailing?: boolean; // fire on the trailing edge (default true)
maxWait?: number; // force a call after at most this many ms
signal?: AbortSignal;
}The returned function carries:
| Member | Description |
| --- | --- |
| cancel() | Drop the pending trailing call without invoking fn. |
| flush() | Invoke a pending call immediately; returns its result. |
| pending() | true if a trailing call is currently scheduled. |
maxWait turns a debounce into a debounce-with-a-ceiling: under constant
pressure fn still runs at least once every maxWait ms.
throttle(fn, wait?, options?)
Invokes fn at most once per wait ms (leading and trailing by default).
import { throttle } from "debouncekit";
const onScroll = throttle(() => measure(), 100);
window.addEventListener("scroll", onScroll);
// drop the trailing call and tear everything down
const ac = new AbortController();
const ping = throttle(send, 1000, { signal: ac.signal });
// ac.abort() → no more pings, pending call cancelledThrottleOptions is { leading?, trailing?, signal? } — same cancel / flush
/ pending controls as debounce.
Notes
- Calls preserve
thisand are invoked with the most recent arguments. - The synchronous return value is the result of the last actual invocation
(like lodash); use
flush()when you need the result right now. - Works anywhere
setTimeoutexists — Node and browsers.
Pairs well with
| Need | Use |
| --- | --- |
| Cap concurrency of async work | runpool |
| Cap rate (token bucket, per second) | ratebucket |
| Retry a flaky async call | retryfn |
| Add a timeout to awaited work | timefence |
Contributors ✨
This project follows the all-contributors specification. Contributions of any kind are welcome — code, docs, bug reports, ideas, reviews! See the emoji key for how each contribution is recognized, and open a PR or issue to get involved.
Thanks goes to these wonderful people:
License
MIT © Tung Tran
