|
export function debounce(fn, delay = 300) {
|
|
let timeoutId;
|
|
return function (...args) {
|
|
clearTimeout(timeoutId);
|
|
timeoutId = setTimeout(() => fn.apply(this, args), delay);
|
|
};
|
|
}
|
|
|
|
export function throttle(fn, delay = 300) {
|
|
let lastCall = 0;
|
|
let timeoutId;
|
|
|
|
return function (...args) {
|
|
const now = Date.now();
|
|
|
|
if (now - lastCall >= delay) {
|
|
lastCall = now;
|
|
fn.apply(this, args);
|
|
} else {
|
|
clearTimeout(timeoutId);
|
|
timeoutId = setTimeout(() => {
|
|
lastCall = Date.now();
|
|
fn.apply(this, args);
|
|
}, delay - (now - lastCall));
|
|
}
|
|
};
|
|
}
|
|
|
|
export function once(fn) {
|
|
let called = false;
|
|
return function (...args) {
|
|
if (!called) {
|
|
called = true;
|
|
return fn.apply(this, args);
|
|
}
|
|
};
|
|
}
|
|
|
|
export function compose(...fns) {
|
|
return (value) => fns.reduceRight((acc, fn) => fn(acc), value);
|
|
}
|
|
|
|
export function pipe(...fns) {
|
|
return (value) => fns.reduce((acc, fn) => fn(acc), value);
|
|
}
|
|
|
|
export async function retry(fn, maxAttempts = 3, delay = 1000) {
|
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
try {
|
|
return await fn();
|
|
} catch (error) {
|
|
if (attempt === maxAttempts) throw error;
|
|
await new Promise(resolve => setTimeout(resolve, delay * attempt));
|
|
}
|
|
}
|
|
}
|
|
|
|
export function deepClone(obj) {
|
|
if (obj === null || typeof obj !== 'object') return obj;
|
|
if (obj instanceof Date) return new Date(obj.getTime());
|
|
if (obj instanceof Array) return obj.map(item => deepClone(item));
|
|
if (obj instanceof Object) {
|
|
const cloned = {};
|
|
for (const key in obj) {
|
|
if (obj.hasOwnProperty(key)) {
|
|
cloned[key] = deepClone(obj[key]);
|
|
}
|
|
}
|
|
return cloned;
|
|
}
|
|
}
|