121 lines
3.1 KiB
JavaScript
121 lines
3.1 KiB
JavaScript
/**
|
|
* Performance Utilities
|
|
*
|
|
* Provides debouncing, throttling, and rate limiting utilities
|
|
* to optimize performance and prevent overwhelming the system
|
|
*/
|
|
|
|
/**
|
|
* Debounce function - delays execution until after wait time has elapsed since last call
|
|
* @param {Function} func - Function to debounce
|
|
* @param {number} wait - Wait time in milliseconds
|
|
* @param {boolean} immediate - Execute on leading edge instead of trailing
|
|
* @returns {Function} Debounced function
|
|
*/
|
|
export function debounce(func, wait, immediate = false) {
|
|
let timeout;
|
|
|
|
return function executedFunction(...args) {
|
|
const context = this;
|
|
|
|
const later = () => {
|
|
timeout = null;
|
|
if (!immediate) func.apply(context, args);
|
|
};
|
|
|
|
const callNow = immediate && !timeout;
|
|
|
|
clearTimeout(timeout);
|
|
timeout = setTimeout(later, wait);
|
|
|
|
if (callNow) func.apply(context, args);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Throttle function - limits execution to once per wait period
|
|
* @param {Function} func - Function to throttle
|
|
* @param {number} wait - Wait time in milliseconds
|
|
* @returns {Function} Throttled function
|
|
*/
|
|
export function throttle(func, wait) {
|
|
let inThrottle;
|
|
let lastFunc;
|
|
let lastRan;
|
|
|
|
return function(...args) {
|
|
const context = this;
|
|
|
|
if (!inThrottle) {
|
|
func.apply(context, args);
|
|
lastRan = Date.now();
|
|
inThrottle = true;
|
|
} else {
|
|
clearTimeout(lastFunc);
|
|
lastFunc = setTimeout(() => {
|
|
if ((Date.now() - lastRan) >= wait) {
|
|
func.apply(context, args);
|
|
lastRan = Date.now();
|
|
}
|
|
}, Math.max(wait - (Date.now() - lastRan), 0));
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Rate Limiter class - limits calls to a function over time
|
|
*/
|
|
export class RateLimiter {
|
|
constructor(maxCalls, timeWindow) {
|
|
this.maxCalls = maxCalls;
|
|
this.timeWindow = timeWindow; // in milliseconds
|
|
this.calls = [];
|
|
}
|
|
|
|
/**
|
|
* Check if a call is allowed
|
|
* @returns {boolean} True if call is allowed
|
|
*/
|
|
allowCall() {
|
|
const now = Date.now();
|
|
|
|
// Remove calls outside the time window
|
|
this.calls = this.calls.filter(time => now - time < this.timeWindow);
|
|
|
|
// Check if we're under the limit
|
|
if (this.calls.length < this.maxCalls) {
|
|
this.calls.push(now);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get time until next call is allowed
|
|
* @returns {number} Milliseconds until next call allowed, or 0 if allowed now
|
|
*/
|
|
getWaitTime() {
|
|
if (this.calls.length < this.maxCalls) {
|
|
return 0;
|
|
}
|
|
|
|
const now = Date.now();
|
|
const oldestCall = this.calls[0];
|
|
return Math.max(0, this.timeWindow - (now - oldestCall));
|
|
}
|
|
|
|
/**
|
|
* Reset the rate limiter
|
|
*/
|
|
reset() {
|
|
this.calls = [];
|
|
}
|
|
}
|
|
|
|
export default {
|
|
debounce,
|
|
throttle,
|
|
RateLimiter
|
|
};
|