104 lines
2.0 KiB
JavaScript
104 lines
2.0 KiB
JavaScript
function debounce(function_, wait = 100, options = {}) {
|
|
if (typeof function_ !== 'function') {
|
|
throw new TypeError(`Expected the first parameter to be a function, got \`${typeof function_}\`.`);
|
|
}
|
|
|
|
if (wait < 0) {
|
|
throw new RangeError('`wait` must not be negative.');
|
|
}
|
|
|
|
// TODO: Deprecate the boolean parameter at some point.
|
|
const {immediate} = typeof options === 'boolean' ? {immediate: options} : options;
|
|
|
|
let storedContext;
|
|
let storedArguments;
|
|
let timeoutId;
|
|
let timestamp;
|
|
let result;
|
|
|
|
function run() {
|
|
const callContext = storedContext;
|
|
const callArguments = storedArguments;
|
|
storedContext = undefined;
|
|
storedArguments = undefined;
|
|
result = function_.apply(callContext, callArguments);
|
|
return result;
|
|
}
|
|
|
|
function later() {
|
|
const last = Date.now() - timestamp;
|
|
|
|
if (last < wait && last >= 0) {
|
|
timeoutId = setTimeout(later, wait - last);
|
|
} else {
|
|
timeoutId = undefined;
|
|
|
|
if (!immediate) {
|
|
result = run();
|
|
}
|
|
}
|
|
}
|
|
|
|
const debounced = function (...arguments_) {
|
|
if (
|
|
storedContext
|
|
&& this !== storedContext
|
|
&& Object.getPrototypeOf(this) === Object.getPrototypeOf(storedContext)
|
|
) {
|
|
throw new Error('Debounced method called with different contexts of the same prototype.');
|
|
}
|
|
|
|
storedContext = this; // eslint-disable-line unicorn/no-this-assignment
|
|
storedArguments = arguments_;
|
|
timestamp = Date.now();
|
|
|
|
const callNow = immediate && !timeoutId;
|
|
|
|
if (!timeoutId) {
|
|
timeoutId = setTimeout(later, wait);
|
|
}
|
|
|
|
if (callNow) {
|
|
result = run();
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
Object.defineProperty(debounced, 'isPending', {
|
|
get() {
|
|
return timeoutId !== undefined;
|
|
},
|
|
});
|
|
|
|
debounced.clear = () => {
|
|
if (!timeoutId) {
|
|
return;
|
|
}
|
|
|
|
clearTimeout(timeoutId);
|
|
timeoutId = undefined;
|
|
};
|
|
|
|
debounced.flush = () => {
|
|
if (!timeoutId) {
|
|
return;
|
|
}
|
|
|
|
debounced.trigger();
|
|
};
|
|
|
|
debounced.trigger = () => {
|
|
result = run();
|
|
|
|
debounced.clear();
|
|
};
|
|
|
|
return debounced;
|
|
}
|
|
|
|
// Adds compatibility for ES modules
|
|
module.exports.debounce = debounce;
|
|
|
|
module.exports = debounce;
|