|
| 1 | +import { execute } from './execute'; |
| 2 | +import { |
| 3 | + memoizationDefaultExpirationMs, |
| 4 | + memoizationKey, |
| 5 | + memoizationMaxExpirationMs, |
| 6 | + MemoizeOptions |
| 7 | +} from '../common/models/executionMemoization.model'; |
| 8 | +import { generateHashId } from '../common/utils/crypto'; |
| 9 | +import { extractFunctionMetadata } from '../common/utils/functionMetadata'; |
| 10 | + |
| 11 | +export function executeMemoize<O>( |
| 12 | + blockFunction: (...params: unknown[]) => Promise<O>, |
| 13 | + inputs?: Array<unknown>, |
| 14 | + options?: MemoizeOptions<O> |
| 15 | +): Promise<Awaited<O>>; |
| 16 | + |
| 17 | +export function executeMemoize<O>( |
| 18 | + blockFunction: (...params: unknown[]) => O, |
| 19 | + inputs?: Array<unknown>, |
| 20 | + options?: MemoizeOptions<O> |
| 21 | +): O; |
| 22 | + |
| 23 | +/** |
| 24 | + * Executes a function with memoization to prevent redundant executions. |
| 25 | + * The result is stored temporarily and cleared after a short delay. |
| 26 | + * |
| 27 | + * @param blockFunction - The function to execute and memoize. |
| 28 | + * @param inputs - Arguments used to generate a unique memoization key. |
| 29 | + * @param options - Additional options including a unique function identifier. |
| 30 | + * @param options.expirationMs - Duration (in milliseconds) before clearing the stored result, |
| 31 | + * capped at 1000ms to prevent excessive retention. |
| 32 | + * @param options.memoizationHandler - Optional callback triggered after checking memoization memory. |
| 33 | + * @returns The memoized result or a newly computed value. |
| 34 | + * |
| 35 | + * @remarks |
| 36 | + * The JavaScript engine may clear the memoized value before another call retrieves it. |
| 37 | + * A short delay (e.g., 100ms) ensures that multiple rapid calls can reuse the stored result. |
| 38 | + * The expiration is capped at 1000ms for efficiency and to avoid unnecessary memory usage. |
| 39 | + */ |
| 40 | +export function executeMemoize<O>( |
| 41 | + blockFunction: (...params: unknown[]) => O | Promise<O>, |
| 42 | + inputs: Array<unknown> = [], |
| 43 | + options: MemoizeOptions<O> |
| 44 | +): Promise<O> | O { |
| 45 | + const expirationMs = Math.min(options.expirationMs ?? memoizationDefaultExpirationMs, memoizationMaxExpirationMs); // Default short delay and Prevent excessive retention |
| 46 | + const memoizationFullStore: Map<string, Map<string, Promise<O> | O>> = (this[memoizationKey] ??= new Map< |
| 47 | + string, |
| 48 | + Map<string, Promise<O> | O> |
| 49 | + >()); |
| 50 | + const memoizationStore = memoizationFullStore.get(options.functionId) ?? new Map<string, Promise<O> | O>(); |
| 51 | + const callId = generateHashId(...inputs); |
| 52 | + const memoizedValue = memoizationStore.get(callId); |
| 53 | + |
| 54 | + if (typeof options.memoizationHandler === 'function') { |
| 55 | + const functionMetadata = extractFunctionMetadata(blockFunction); |
| 56 | + options.memoizationHandler({ metadata: functionMetadata, callId, isMemoized: !!memoizedValue, value: memoizedValue }); |
| 57 | + } |
| 58 | + |
| 59 | + if (memoizedValue) { |
| 60 | + return memoizedValue; |
| 61 | + } else { |
| 62 | + const callResponseOrPromise = (execute.bind(this) as typeof execute)(blockFunction.bind(this) as typeof blockFunction, inputs); |
| 63 | + memoizationStore.set(callId, callResponseOrPromise); |
| 64 | + this[memoizationKey].set(options.functionId, memoizationStore); |
| 65 | + |
| 66 | + if (callResponseOrPromise instanceof Promise) { |
| 67 | + callResponseOrPromise.finally(() => setTimeout(() => memoizationStore.delete(callId), expirationMs)); |
| 68 | + } else { |
| 69 | + setTimeout(() => memoizationStore.delete(callId), expirationMs); |
| 70 | + } |
| 71 | + return callResponseOrPromise; |
| 72 | + } |
| 73 | +} |
0 commit comments