Throttle with Leading and Trailing Edges
A leading-only throttle drops the last call's arguments; a trailing-only throttle feels laggy on the first event. The Lodash-style throttle that fires on BOTH edges is the version every UI codebase eventually wants: an immediate response on the leading edge plus a guaranteed final fire after the burst ends. This snippet builds that production-grade throttle from scratch with cancel and flush, then shows the configurable `leading` / `trailing` toggle that powers most real-world helpers.
158 views
5
function throttleLT(fn, wait) {
let timer = null;
let lastCall = 0;
let pendingArgs = null;
let pendingThis = null;
function invoke() {
lastCall = Date.now();
timer = null;
const args = pendingArgs;
const thisArg = pendingThis;
pendingArgs = null;
pendingThis = null;
if (args) fn.apply(thisArg, args);
}
return function throttled(...args) {
const now = Date.now();
const remaining = wait - (now - lastCall);
pendingArgs = args;
pendingThis = this;
if (remaining <= 0 || remaining > wait) {
// Leading edge: enough time has passed (or clock skew). Fire now.
if (timer) {
clearTimeout(timer);
timer = null;
}
invoke();
} else if (!timer) {
// Schedule trailing-edge fire at the end of the current window.
timer = setTimeout(invoke, remaining);
}
};
}
const log = throttleLT((label, t) => console.log(label, t), 30);
log('A', Date.now()); // fires immediately (leading)
log('B', Date.now()); // queued for trailing
log('C', Date.now()); // overwrites the queued args
// After ~30ms: fires once with CThe contract of leading-plus-trailing is: fire on the very first call in any quiet window, then guarantee a final fire with the latest arguments at the end of any burst. Tracking lastCall against Date.now() lets the leading-edge branch fire only when the previous fire happened long enough ago. A second setTimeout schedules the trailing fire at the exact end of the current window, using wait - (now - lastCall) so the gap stays exactly wait between fires regardless of when in the window the burst arrived. The remaining > wait guard handles the edge case where the system clock moved backwards (NTP correction, debugger pause).
1 more snippet in this entry are available for premium members.
Upgrade to Premium