Skip to content

Instantly share code, notes, and snippets.

@westc
Created August 28, 2026 19:51
Show Gist options
  • Select an option

  • Save westc/a495c685c45649609c22ab422b7539a0 to your computer and use it in GitHub Desktop.

Select an option

Save westc/a495c685c45649609c22ab422b7539a0 to your computer and use it in GitHub Desktop.
Creates a debounced function with an immediate side-effect callback and a maximum wait time (throttle) ceiling.
/**
* Creates a debounced function with an immediate side-effect callback
* and a maximum wait time (throttle) ceiling.
*
* @param {Object} options - Configuration options for the debounce.
* @param {number} options.waitMS - The delay in milliseconds to wait after the
* last call before execution.
* @param {Function} options.afterWait - The primary callback executed after the
* debounce or maxWait period.
* @param {Function} [options.immediate] - Optional function invoked immediately
* on every call (errors are caught).
* @param {number} [options.maxWaitMS] - The maximum delay in milliseconds
* allowed before execution is forced.
* @returns {Function} The new debounced wrapper function.
*/
function debounce({ waitMS, afterWait, immediate, maxWaitMS }) {
// Validate required parameters
if ((afterWait ?? null) === null) throw new Error('"afterWait" not provided.');
if ((waitMS ?? null) === null) throw new Error('"waitMS" not provided.');
let timeoutID, maxTimeoutID, maxTimeoutArgs;
return function(...args) {
// 1. Execute immediate callback on every invocation, suppressing any errors
try {
immediate?.apply(this, args);
} catch { }
// 2. Handle the maximum wait ceiling if configured
if (maxWaitMS) {
if (!maxTimeoutArgs) {
maxTimeoutID = setTimeout(
() => {
clearTimeout(timeoutID);
[args, maxTimeoutArgs] = [maxTimeoutArgs, null];
afterWait.apply(this, args);
},
maxWaitMS
);
}
// Capture the latest arguments during the active maxWait window
maxTimeoutArgs = args;
}
// 3. Reset and start the standard sliding debounce delay timer
timeoutID = setTimeout(
() => {
clearTimeout(maxTimeoutID);
maxTimeoutArgs = null;
afterWait.apply(this, args);
},
waitMS
);
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment