Throttle Calls With Leading and Trailing
Limit function execution frequency with configurable leading and trailing edges, plus cancel and flush controls for cleanup.
Code
export function throttle(fn, wait, options = {}) {
if (typeof fn !== "function") throw new TypeError("fn must be a function");
if (!Number.isFinite(wait) || wait < 0) throw new RangeError("wait must be a non-negative number");
const { leading = true, trailing = true } = options;
let timer = null;
let lastInvoke = 0;
let pendingArgs = null;
let pendingThis = null;
let started = false;
function invoke() {
const args = pendingArgs;
const context = pendingThis;
pendingArgs = null;
pendingThis = null;
if (args === null) return;
lastInvoke = Date.now();
fn.apply(context, args);
}
function schedule(delay) {
if (timer !== null) return;
timer = setTimeout(() => {
timer = null;
if (trailing) invoke();
}, delay);
}
function throttled(...args) {
const now = Date.now();
if (!started) {
started = true;
lastInvoke = now;
if (leading) {
fn.apply(this, args);
} else {
pendingArgs = args;
pendingThis = this;
schedule(wait);
}
return;
}
const elapsed = now - lastInvoke;
if (elapsed >= wait) {
if (timer !== null) {
clearTimeout(timer);
timer = null;
}
lastInvoke = now;
pendingArgs = null;
pendingThis = null;
fn.apply(this, args);
return;
}
if (trailing) {
pendingArgs = args;
pendingThis = this;
schedule(wait - elapsed);
}
}
throttled.cancel = () => {
if (timer !== null) clearTimeout(timer);
timer = null;
pendingArgs = null;
pendingThis = null;
started = false;
lastInvoke = 0;
};
throttled.flush = () => {
if (timer !== null) {
clearTimeout(timer);
timer = null;
}
invoke();
};
return throttled;
}
const send = throttle((value) => console.log("sent", value), 200, {
leading: true,
trailing: true,
});
send(1);
send(2);
setTimeout(() => send(3), 250);Report a problem with this exampleOpens a pre-filled issue on GitHub.