ai-patterns
Version:
Production-ready TypeScript patterns to build solid and robust AI applications. Retry logic, circuit breakers, rate limiting, human-in-the-loop escalation, prompt versioning, response validation, context window management, and more—all with complete type
128 lines • 3.9 kB
JavaScript
;
/**
* Debounce Pattern - Execute after silence period
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.debounce = void 0;
exports.defineDebounce = defineDebounce;
const common_1 = require("../types/common");
/**
* Define a debounced function
*
* @example
* ```typescript
* const saveData = defineDebounce({
* execute: async () => await api.save(),
* wait: 500,
* maxWait: 2000
* });
*
* saveData(); // Will execute after 500ms of silence
* ```
*/
function defineDebounce(options) {
const { execute: fn, wait, maxWait, leading = false, logger = common_1.defaultLogger, onDebounced, onExecute, } = options;
// Support both 'wait' and 'delayMs' for compatibility
const delay = wait ?? options.delayMs ?? 300;
let timeoutId = null;
let maxWaitTimeoutId = null;
let lastInvokeTime = 0;
let lastArgs = null;
let lastThis = null;
let result;
let pending = false;
let pendingResolvers = [];
const invokeFunction = async () => {
const args = lastArgs;
const thisArg = lastThis;
const resolvers = [...pendingResolvers];
lastArgs = null;
lastThis = null;
lastInvokeTime = Date.now();
pending = false;
pendingResolvers = [];
if (onExecute) {
onExecute(...args);
}
logger.info("Executing debounced function");
result = await fn.apply(thisArg, args);
// Resolve all pending promises
resolvers.forEach(resolve => resolve(result));
return result;
};
const cancelMaxWaitTimer = () => {
if (maxWaitTimeoutId) {
clearTimeout(maxWaitTimeoutId);
maxWaitTimeoutId = null;
}
};
const cancelTimer = () => {
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
};
const debounced = function (...args) {
const time = Date.now();
const isInvoking = leading && !pending;
lastArgs = args;
lastThis = this;
pending = true;
if (isInvoking) {
return invokeFunction();
}
cancelTimer();
if (onDebounced) {
onDebounced();
}
return new Promise((resolve) => {
// Add this resolver to pending resolvers
pendingResolvers.push(resolve);
// Set timeout for this debounced call (always create new timeout after cancel)
timeoutId = setTimeout(async () => {
timeoutId = null;
cancelMaxWaitTimer();
await invokeFunction();
}, delay);
// Max wait timer
if (maxWait !== undefined && !maxWaitTimeoutId) {
const timeSinceLastInvoke = time - lastInvokeTime;
const timeToMaxWait = maxWait - timeSinceLastInvoke;
if (timeToMaxWait > 0) {
maxWaitTimeoutId = setTimeout(async () => {
maxWaitTimeoutId = null;
cancelTimer();
await invokeFunction();
}, timeToMaxWait);
}
}
});
};
debounced.cancel = () => {
cancelTimer();
cancelMaxWaitTimer();
lastArgs = null;
lastThis = null;
pending = false;
pendingResolvers = [];
logger.info("Debounced function cancelled");
};
debounced.flush = async () => {
if (!pending) {
return result;
}
cancelTimer();
cancelMaxWaitTimer();
return await invokeFunction();
};
debounced.pending = () => {
return pending;
};
return debounced;
}
/**
* @deprecated Use `defineDebounce` instead
* @see defineDebounce
*/
exports.debounce = defineDebounce;
//# sourceMappingURL=debounce.js.map