throttle-debounce
Lightweight throttle and debounce functions for rate-limiting JavaScript event handlers.
Repository Health
Technical Analysis
throttle-debounce is a small, dependency-free JavaScript utility providing two rate-limiting primitives: throttle, which caps how often a callback can run over a series of calls, and debounce, which waits for a pause in calls before running the callback once. It began as an ES Modules/CommonJS port of the classic jquery-throttle-debounce plugin, with the jQuery dependency removed, and ships as ESM, CJS, and UMD builds so it drops into any module system or a plain <script> tag.
Both functions return a wrapper with leading/trailing-edge control (noLeading, noTrailing, atBegin) and an attached cancel() method (with an upcomingOnly option to cancel just the next pending call), making it a common choice for taming resize, scroll, and input handlers without pulling in a larger utility library.
What You Get
throttle(delay, callback, options)— rate-limits a callback to run at most once perdelayms, with leading/trailing-edge control vianoLeading/noTrailingdebounce(delay, callback, options)— delays a callback untildelayms have passed since the last call, with anatBeginoption to fire on the leading edge instead- A
cancel()method attached to every throttled/debounced function, including anupcomingOnlymode that cancels only the next scheduled call - Prebuilt ESM, CommonJS, and UMD distributions so the package works with
import,require, or a plain<script>tag via jsDelivr/unpkg - Zero runtime dependencies and a tiny footprint, suitable for both browser and Node.js environments
Common Use Cases
- Throttling
resizeorscrollevent handlers so expensive layout or measurement code doesn’t run on every event - Debouncing search-as-you-type input handlers so an API call or filter only fires once typing pauses
- Rate-limiting analytics or logging calls triggered by frequent user interactions
- Cancelling a pending debounced save or autosave call when a component unmounts or the user navigates away
Under The Hood
Architecture
The package is deliberately flat: throttle.js holds the single core implementation — a closure that tracks lastExec and a timeoutID, and returns a wrapper function with a cancel method attached — while debounce.js is a thin adapter that simply calls throttle with debounceMode set from the atBegin option, reusing the same state machine for both behaviors. index.js just re-exports both. There are no layers, no dependency injection, and no internal abstractions beyond this one shared closure; changing the timing logic in throttle.js’s wrapper function is the single point that would affect both throttling and debouncing behavior.
Tech Stack
The source is plain ES2015+ JavaScript with JSDoc annotations (no TypeScript). Babel (@babel/preset-env plus @babel/plugin-transform-runtime) transpiles per the project’s Browserslist config, and Rollup (rollup.config.js) builds the cjs/, esm/, and umd/ output bundles declared in package.json’s exports map. Tests run through Karma against real browsers (Chrome, Firefox, and BrowserStack-hosted browsers in CI) using the QUnit test framework. Linting is ESLint with eslint-config-nitpick plus eslint-config-prettier/eslint-plugin-prettier, enforced pre-commit via Husky and lint-staged, and releases are cut with np.
Code Quality
test/index.js is a substantial suite (adapted from the original jquery-throttle-debounce QUnit tests) that exercises leading/trailing-edge combinations, cancellation, and timing edge cases by driving the throttled/debounced functions through repeated timed calls. GitHub Actions runs lint and the Karma test suite (with a headless-browser fallback when BrowserStack secrets aren’t available) on every push and PR. There is no TypeScript and no bundled .d.ts file, so type safety relies entirely on the JSDoc comments in throttle.js and debounce.js; error handling is minimal because the functions have no failure modes to guard against beyond normal argument passing.
API Design
The public surface is exactly two named exports with a shared options-object shape (noLeading/noTrailing/debounceMode for throttle, atBegin for debounce), so switching between throttling and debouncing a given callback requires no boilerplate beyond swapping the function name. The attached cancel() method (with an upcomingOnly variant) is a convenient, low-ceremony way to stop pending executions without holding onto a separate timer reference, and the README documents every option with runnable examples.