Modulator
Modulator is an advanced debouncing utility designed to optimize high-frequency events in web applications, such as scroll, resize, and input. This standalone solution surpasses other debouncing functions like Lodash and Underscore in terms of performance and flexibility. Key features include parameter validation, cache, and result storage, control over cache size, a maximum wait time, and a Promise-based return.
By incorporating a cache system for debounced function call results, Modulator allows users to control the cache size through the maxCacheSize parameter, optimizing performance and memory usage. The Promise-based return simplifies the handling and tracking of function call outcomes. Additionally, the module includes a cancel method for aborting the debounced function execution when necessary and a result method for retrieving the result of the last execution. These features provide enhanced control and flexibility for developers, making Modulator a superior choice for debouncing solutions in web applications.
Demo
API Documentation
To initiate, install Modulator
using NPM:
npm i @danielhaim/modulator
Module Example
import Modulator from "./path/to/danielhaim/modulator";
Modulator.modulate(func, wait, immediateopt, contextopt, maxCacheSizeopt, maxWaitopt) → {function}
Modulator.modulate()
Creates a debounced function with a configurable cache and maximum wait time. The debounced function delays invoking func
until after wait
milliseconds have elapsed since the last time the debounced function was invoked. The function also caches its results based on the arguments passed. If immediate
is true, it triggers the function on the leading edge, instead of the trailing.
Parameters
Name | Type | Attributes | Default | Description |
---|
func | function | | | The function to debounce. |
wait | number | | | The debouncing wait time in milliseconds. |
immediate | boolean | <optional> | false | Flag to determine if the function should be executed immediately. |
context | Object | <optional> | null | The context in which the function should be executed. |
maxCacheSize | number | <optional> | 100 | The maximum cache size for storing results. |
maxWait | number | null | <optional> | null |
Enhanced Functionality
Once you create a debounced function using modulate
, it comes with additional methods that enhance its capabilities:
debounced.cancel()
: Cancels the execution of the debounced function. This is useful if you need to prevent the function from being called if certain conditions are met.debounced.result()
: Returns an array of the results from all the invocations of the debounced function. It provides a way to access the outcomes of the function calls, especially useful when dealing with asynchronous operations.
These methods provide greater control and flexibility in managing the debounced function, allowing for more sophisticated usage patterns in your applications.
Examples
The package can be imported and used in both Node.js and browser environments using the following syntax:
<script src="./path/to/modulator.amd.js"></script>
<script>
const debouncedFunc = Modulator.modulate(originalFunc, 1000);
</script>
In the example Below, debouncedFunc
is a debounced version of originalFunc
. The function originalFunc
will be invoked no more than once every 1000 milliseconds (1 second).
import Modulator from "./path/to/danielhaim/modulator";
const debouncedFunction = Modulator.modulate(myFunction, 100);
element.addEventListener('mousemove', debouncedFunction);
const originalFunc = () => {
console.log('Original function called');
};
const debouncedFunc = Modulator.modulate(originalFunc, 1000);
debouncedFunc();
debouncedFunc();
debouncedFunc();
debouncedFunc();
debouncedFunc();
Debouncing a resize event listener
function handleResize(event) {
}
const debouncedHandleResize = Modulator.modulate(handleResize, 100);
window.addEventListener('resize', debouncedHandleResize);
Debouncing a form submission
function handleSubmit(event) {
event.preventDefault();
}
const debouncedHandleSubmit = Modulator.modulate(handleSubmit, 1000, true);
document.querySelector('#my-form').addEventListener('submit', debouncedHandleSubmit);
Debouncing a search function
function handleSearch(query) {
}
const debouncedHandleSearch = Modulator.modulate(handleSearch, 500);
document.querySelector('#search-input').addEventListener('input', (event) => {
debouncedHandleSearch(event.target.value);
});
Debouncing a mouseover event listener
function handleMouseover(event) {
}
const debouncedHandleMouseover = Modulator.modulate(handleMouseover, 250);
document.querySelector('#my-element').addEventListener('mouseover', debouncedHandleMouseover);
Debouncing a resize event listener with a maximum wait time
function handleResize(event) {
}
const debouncedHandleResize = Modulator.modulate(handleResize, 100, false, null, 10, 500);
window.addEventListener('resize', debouncedHandleResize);
Debouncing a form submission
function handleSubmit(event) {
event.preventDefault();
}
const debouncedHandleSubmit = Modulator.modulate(handleSubmit, 1000, true);
document.querySelector('#my-form').addEventListener('submit', debouncedHandleSubmit);
Debouncing a search function
function handleSearch(query) {
}
const debouncedHandleSearch = Modulator.modulate(handleSearch, 500);
document.querySelector('#search-input').addEventListener('input', (event) => {
debouncedHandleSearch(event.target.value);
});
Debouncing a mouseover event listener
function handleMouseover(event) {
}
const debouncedHandleMouseover = Modulator.modulate(handleMouseover, 250);
document.querySelector('#my-element').addEventListener('mouseover', debouncedHandleMouseover);
Advanced Examples
Debouncing a function with cache
function fetchData(query) {
console.log(`Fetching data for query: ${query}`);
return Promise.resolve(`Data for "${query}"`);
}
const debouncedFetchData = Modulator.modulate(fetchData, 500, false, null, 2);
debouncedFetchData('apple');
debouncedFetchData('apple');
debouncedFetchData('banana');
setTimeout(() => {
debouncedFetchData('apple').then(console.log);
debouncedFetchData('banana').then(console.log);
}, 1000);
Debouncing a Function with Result Aggregation
In this example, originalFunc
calculates the sum of two numbers. We debounce this function using modulate
. Despite multiple calls to the debounced function with the same arguments within a short interval, it only executes once after the 1000ms wait time. The result method then retrieves the result
of the debounced function's last execution, which is [3]
in this case.
const originalFunc = (x, y) => x + y;
const debouncedFunc = Modulator.modulate(originalFunc, 1000);
const results = [];
for (let i = 0; i < 3; i++) {
results.push(debouncedFunc(1, 2));
}
setTimeout(() => {
Promise.all(results).then(values => {
console.log('Results of each debounced call:', values);
console.log('Result from the debounced function\'s last execution:', debouncedFunc.result());
});
}, 2000);
Debouncing a function with cache
Here's an example of creating a debounced version of a function fetchData
with a cache size of 2. The debounced function debouncedFetchData
is called multiple times with the same query ('apple' and 'banana'). Still, the original part is only invoked for the last two calls (one for 'apple' and one for 'banana'). After 1 second, the function is called again for 'apple' and 'banana,' and the results are retrieved from the cache instead of invoking the original function.
import Modulator from "@danielhaim/modulator";
function fetchData(query) {
console.log(`Fetching data for query: ${query}`);
return Promise.resolve(`Data for "${query}"`);
}
const debouncedFetchData = Modulator.modulate(fetchData, 500, false, null, 2);
debouncedFetchData('apple');
debouncedFetchData('apple');
debouncedFetchData('apple');
debouncedFetchData('banana');
debouncedFetchData('banana');
setTimeout(() => {
debouncedFetchData('apple');
debouncedFetchData('banana');
}, 1000);
maxCacheSize Parameter
The modulate function includes a maxCacheSize
parameter that allows you to control the cache size of the debounced function. This parameter specifies the maximum number of function results that should be cached. Once the cache size is reached, the oldest result will be removed to accommodate the new result. If maxCacheSize
is set to null
or undefined,
the cache will have unlimited size.
Caching results
In this example, the memoize
function creates a cached version of a function to return the same result for the same arguments, improving performance by avoiding unnecessary function calls.
const cache = new Map();
const memoize = func => {
return function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = func(...args);
cache.set(key, result);
return result;
};
};
const originalFunc = x => x + 1;
const memoizedFunc = memoize(originalFunc);
const result1 = memoizedFunc(1);
const result2 = memoizedFunc(1);
const result3 = memoizedFunc(1);
Tests
Test Errors
✓ Should throw an error if the first parameter is not a function (2 ms)
✓ Should throw an error if the second parameter is not a number (1 ms)
✓ Should throw an error if the third parameter is not a boolean
✓ Should throw an error if the fifth parameter is not a number (1 ms)
✓ Should throw an error if the sixth parameter is not a number or null (1 ms)
✓ Should throw an error if the sixth parameter is less than the second parameter
Test Parameters
✓ Should debounce the original function (1 ms)
✓ Should delay execution by maxWait time (1 ms)
✓ Should cache results for the same arguments (1 ms)
✓ Should debounce a function and return a debounced function
✓ Should debounce and cache the results of the original function
Test EventListeners
✓ Should trigger mouseover event (1 ms)
✓ Should trigger window resize event
Resources
Report Bugs
If you encounter any bugs while using Modulator, please report them to the GitHub issue tracker.
When submitting a bug report, please include as much information as possible.