export function debounce( func: (...e: T) => R, wait = 1000, immediate = false ) { // 'private' variable for instance // The returned function will be able to reference this due to closure. // Each call to the returned function will share this common timer. let timeout: NodeJS.Timeout | null; // Calling debounce returns a new anonymous function return function () { // reference the context and args for the setTimeout function const args = arguments; // Should the function be called now? If immediate is true // and not already in a timeout then the answer is: Yes const callNow = immediate && !timeout; // This is the basic debounce behavior where you can call this // function several times, but it will only execute once // [before or after imposing a delay]. // Each time the returned function is called, the timer starts over. clearTimeout(timeout ?? undefined); // Set the new timeout timeout = setTimeout(function () { // Inside the timeout function, clear the timeout variable // which will let the next execution run when in 'immediate' mode timeout = null; // Check if the function already ran with the immediate flag if (!immediate) { // Call the original function with apply // apply lets you define the 'this' object as well as the arguments // (both captured before setTimeout) func.apply(this, args); } }, wait); // Immediate mode and no wait timer? Execute the function.. if (callNow) func.apply(this, args); } as (...e: T) => R; }