export type AnyFunction = (...args: any[]) => any export type LazyCallbackReturnType = | Parameters | [] export interface LazyCallback { (...args: Parameters): void invoked(): Promise> } export interface LazyCallbackOptions { maxCalls?: number maxCallsCallback?(): void } export function createLazyCallback( options: LazyCallbackOptions = {} ): LazyCallback { let calledTimes = 0 let autoResolveTimeout: NodeJS.Timeout let remoteResolve: (args: LazyCallbackReturnType) => unknown const callPromise = new Promise>((resolve) => { remoteResolve = resolve }).finally(() => { clearTimeout(autoResolveTimeout) }) const fn: LazyCallback = function (...args) { if (options.maxCalls && calledTimes >= options.maxCalls) { options.maxCallsCallback?.() } remoteResolve(args) calledTimes++ } fn.invoked = async () => { // Immediately resolve the callback if it hasn't been called already. autoResolveTimeout = setTimeout(() => { remoteResolve([]) }, 0) return callPromise } return fn }