| /** | |
| * Creates a function that samples calls at regular intervals and captures trailing calls. | |
| * - Drops calls that occur between sampling intervals | |
| * - Takes one call per sampling interval if available | |
| * - Captures the last call if no call was made during the interval | |
| * | |
| * @param fn The function to sample | |
| * @param sampleInterval How often to sample calls (in ms) | |
| * @returns The sampled function | |
| */ | |
| export function createSampler<T extends (...args: any[]) => any>(fn: T, sampleInterval: number): T { | |
| let lastArgs: Parameters<T> | null = null; | |
| let lastTime = 0; | |
| let timeout: NodeJS.Timeout | null = null; | |
| // Create a function with the same type as the input function | |
| const sampled = function (this: any, ...args: Parameters<T>) { | |
| const now = Date.now(); | |
| lastArgs = args; | |
| // If we're within the sample interval, just store the args | |
| if (now - lastTime < sampleInterval) { | |
| // Set up trailing call if not already set | |
| if (!timeout) { | |
| timeout = setTimeout( | |
| () => { | |
| timeout = null; | |
| lastTime = Date.now(); | |
| if (lastArgs) { | |
| fn.apply(this, lastArgs); | |
| lastArgs = null; | |
| } | |
| }, | |
| sampleInterval - (now - lastTime), | |
| ); | |
| } | |
| return; | |
| } | |
| // If we're outside the interval, execute immediately | |
| lastTime = now; | |
| fn.apply(this, args); | |
| lastArgs = null; | |
| } as T; | |
| return sampled; | |
| } | |