Source: Debounce.ts

const timeoutIds: Record<string | number, NodeJS.Timeout> = {};

export function updateMakeupDebounce(
    id: number,
    fn: CallableFunction,
    delay: number,
    args: unknown[],
) {
    const timeoutId = timeoutIds[id];
    if (timeoutId) clearTimeout(timeoutId);
    timeoutIds[id] = setTimeout(() => {
        delete timeoutIds[id];
        fn(...args);
    }, delay);
}

/**
 * 
 * @param id effect.id or productUsagePattern.id, it's a mix of domains for this function.
 * As things stand, it makes no sense to have both codepaths (MakeupEngine and CMSMakeupEngine) used at the same time.
 * So problems/clashes should not occur, but this is still semantically unsound
 * @param fn action to invoke
 * @param delay in ms
 */
export function updateMakeupDebounce2(
    id: number,
    fn: VoidFunction,
    delay: number,
) {
    const timeoutId = timeoutIds[id];
    if (timeoutId) clearTimeout(timeoutId);
    timeoutIds[id] = setTimeout(() => {
        delete timeoutIds[id];
        fn();
    }, delay);
}