if (global.wrappedTimers === undefined) { global.wrappedTimers = new Map(); } export type WrappedTimer = { timer: Timer | undefined; running: boolean; }; export type WrappedTimerResult = { callback: ReturnType; wrappedTimer: WrappedTimer; }; /** * Create a callback handler for a wrapped timer that prevents the callback from running if it is already running * and prevents the callback from running if it is already running. * @template TArgs * @param {string} key unique identifier for the timer * @param {(...args: Array) => (Promise | void)} callback function to run * @param {Array} args arguments to pass to the callback * @returns {() => Promise} */ function getCallbackHandler(key: string, callback: (...args: Array) => (Promise | void), ...args: Array): () => Promise { return async () => { const thisTimer = global.wrappedTimers.get(key); if (thisTimer === undefined) { console.debug(`Wrapped timer ${key} does not exist`); return; } if (thisTimer.running) { console.debug(`Wrapped timer ${key} is already running`); return; } try { thisTimer.running = true; await callback(...args); } catch (e) { console.error(e); } finally { thisTimer.running = false; } }; } /** * Create a wrapped timer aka interval * @template TArgs * @param {string} key unique identifier for the timer * @param {number} interval in milliseconds * @param {(...args: Array) => (Promise | void)} callback function to run * @param {Array} args arguments to pass to the callback * @returns {WrappedTimerResult} */ export function createWrappedTimer(key: string, callback: (...args: Array) => (Promise | void), interval: number, ...args: Array): WrappedTimerResult { const thisTimer = global.wrappedTimers.get(key); const handler = getCallbackHandler(key, callback, ...args); if (thisTimer !== undefined) { console.debug(`Wrapped timer ${key} already exists, clearing timer`); clearInterval(thisTimer.timer); console.debug(`Wrapped timer ${key} set with interval ${interval}ms`); thisTimer.timer = setInterval(handler, interval); return { callback: handler, wrappedTimer: thisTimer, }; } console.debug(`Wrapped timer ${key} created with interval ${interval}ms`); const wrappedTimer: WrappedTimer = { timer: setInterval(handler, interval), running: false, }; global.wrappedTimers.set(key, wrappedTimer); return { callback: handler, wrappedTimer, }; }