@modern-kit/utils
Version:
1 lines • 8.45 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../../../src/common/debounce/index.ts"],"sourcesContent":["interface DebounceOptions {\n signal?: AbortSignal;\n maxWait?: number;\n leading?: boolean;\n trailing?: boolean;\n}\n\ninterface DebouncedFunction<F extends (...args: any[]) => void> {\n (...args: Parameters<F>): void;\n cancel: () => void;\n flush: () => void;\n}\n\n/**\n * @description 디바운스된 함수를 생성합니다.\n *\n * 디바운스된 함수는 마지막으로 호출된 시점으로부터 `wait` 밀리초가 경과할 때까지 제공된 함수의 실행을 지연시킵니다.\n * - 연속 호출 시 이전 호출은 취소되고 새로운 타이머가 시작됩니다.\n *\n * @template F - 함수의 타입입니다.\n * @param {F} func - 디바운스할 함수입니다.\n * @param {number} wait - 지연시킬 시간(밀리초)입니다.\n * @param {DebounceOptions} options - 옵션 객체입니다.\n * @param {number} options.maxWait - 최대 대기 시간(밀리초)입니다.\n * debounce는 기본적으로 호출되면 대기 시간이 초기화됩니다. maxWait 옵션은 연속적인 호출이 발생하더라도 첫 호출 기준으로 최대 대기 시간이 지나면 강제로 함수가 실행됩니다.\n * @param {boolean} options.leading - 첫 번째 호출을 실행할지 여부입니다.\n * @param {boolean} options.trailing - 마지막 호출을 실행할지 여부입니다.\n * @param {AbortSignal} options.signal - 디바운스된 함수를 취소하기 위한 선택적 AbortSignal입니다.\n *\n * @returns {DebouncedFunction<F>} `cancel` 메서드가 포함된 새로운 디바운스된 함수를 반환합니다.\n * - `cancel` 메서드는 디바운스된 함수의 대기 중인 실행을 취소합니다.\n * - `flush` 메서드는 대기 중인 실행이 있는 경우 디바운스된 함수를 즉시 실행합니다.\n *\n * @example\n * const debouncedFunction = debounce(func, 1000);\n * debouncedFunction(); // 1초 후에 함수가 실행됩니다\n *\n * @example\n * // AbortSignal 사용 예시\n * const controller = new AbortController();\n * const signal = controller.signal;\n * const debouncedWithSignal = debounce(func, 1000, { signal });\n *\n * debouncedWithSignal();\n *\n * // 디바운스된 함수 호출을 취소합니다\n * controller.abort();\n */\nexport function debounce<F extends (...args: any[]) => void>(\n func: F,\n wait: number,\n options: DebounceOptions = {}\n): DebouncedFunction<F> {\n let pendingThis: any = undefined;\n let pendingArgs: Parameters<F> | null = null;\n let pendingAt: number | null = null;\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const { signal, leading = false, trailing = true, maxWait } = options;\n\n /**\n * @description 대기 중인 함수를 실행합니다.\n *\n * pendingArgs가 존재하는 경우에만 함수를 실행하고(즉, debounced 함수가 호출된 경우), 실행 후에는 관련 상태를 초기화합니다.\n */\n const invoke = () => {\n if (pendingArgs != null) {\n func.apply(pendingThis, pendingArgs);\n pendingThis = undefined;\n pendingArgs = null;\n pendingAt = null;\n }\n };\n\n /**\n * @description 타이머가 종료될 때 호출되는 함수입니다.\n *\n * trailing 옵션이 true인 경우 대기 중인 함수를 실행하고, 모든 상태를 초기화합니다.\n */\n const onTimerEnd = () => {\n if (trailing) {\n invoke();\n }\n cancel();\n };\n\n /**\n * @description 다음 디바운스 실행까지의 대기 시간을 계산하고 반환합니다.\n *\n * maxWait 옵션은 함수가 실행되지 않고 대기할 수 있는 최대 시간을 제한합니다.\n * 예를 들어, wait이 100ms이고 maxWait이 300ms인 경우:\n * - 연속적인 호출이 발생하더라도 첫 호출로부터 300ms가 지나면 강제로 함수가 실행됩니다\n * - 이는 함수가 너무 오랫동안 실행되지 않는 것을 방지합니다\n */\n const getWaitTime = () => {\n if (typeof maxWait === 'number' && pendingAt != null) {\n const timeSinceFirstCall = Date.now() - pendingAt;\n const remainingMaxWait = maxWait - timeSinceFirstCall;\n\n if (remainingMaxWait <= 0) {\n onTimerEnd();\n return;\n }\n\n return Math.min(wait, remainingMaxWait);\n }\n\n return wait;\n };\n\n /**\n * @description 디바운스 타이머를 스케줄링합니다.\n *\n * 기존 타이머가 있다면 취소하고, 새로운 타이머를 설정합니다.\n * getWaitTime()을 통해 계산된 대기 시간 후에 onTimerEnd를 실행합니다.\n */\n const schedule = () => {\n if (timeoutId != null) {\n clearTimeout(timeoutId);\n }\n\n const waitTime = getWaitTime();\n\n timeoutId = setTimeout(() => {\n timeoutId = null;\n onTimerEnd();\n }, waitTime);\n };\n\n /**\n * @description 현재 실행 중인 타이머를 취소합니다.\n *\n * 타이머가 존재하는 경우 clearTimeout으로 취소하고\n * timeoutId를 null로 초기화합니다.\n */\n const cancelTimer = () => {\n if (timeoutId != null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n };\n\n /**\n * @description 디바운스된 함수를 취소합니다.\n */\n const cancel = () => {\n cancelTimer();\n pendingThis = undefined;\n pendingArgs = null;\n pendingAt = null;\n };\n\n /**\n * @description 디바운스된 함수를 즉시 실행합니다. 이때, 타이머를 취소하고, 대기 중인 함수를 실행합니다.\n */\n const flush = () => {\n cancelTimer();\n invoke();\n pendingAt = null;\n };\n\n const debounced = function (this: any, ...args: Parameters<F>) {\n if (signal?.aborted) {\n return;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n pendingThis = this;\n pendingArgs = args;\n\n const isFirstCall = timeoutId == null;\n\n if (isFirstCall) {\n pendingAt = Date.now();\n }\n\n schedule();\n\n if (leading && isFirstCall) {\n invoke();\n }\n };\n\n debounced.cancel = cancel;\n debounced.flush = flush;\n\n signal?.addEventListener('abort', cancel, { once: true });\n\n return debounced;\n}\n"],"names":[],"mappings":";;AAgDO,SAAS,QAAA,CACd,IAAA,EACA,IAAA,EACA,OAAA,GAA2B,EAAC,EACN;AACtB,EAAA,IAAI,WAAA,GAAmB,MAAA;AACvB,EAAA,IAAI,WAAA,GAAoC,IAAA;AACxC,EAAA,IAAI,SAAA,GAA2B,IAAA;AAC/B,EAAA,IAAI,SAAA,GAAkD,IAAA;AAEtD,EAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,GAAU,OAAO,QAAA,GAAW,IAAA,EAAM,SAAQ,GAAI,OAAA;AAO9D,EAAA,MAAM,SAAS,MAAM;AACnB,IAAA,IAAI,eAAe,IAAA,EAAM;AACvB,MAAA,IAAA,CAAK,KAAA,CAAM,aAAa,WAAW,CAAA;AACnC,MAAA,WAAA,GAAc,MAAA;AACd,MAAA,WAAA,GAAc,IAAA;AACd,MAAA,SAAA,GAAY,IAAA;AAAA,IACd;AAAA,EACF,CAAA;AAOA,EAAA,MAAM,aAAa,MAAM;AACvB,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,MAAA,EAAO;AAAA,IACT;AACA,IAAA,MAAA,EAAO;AAAA,EACT,CAAA;AAUA,EAAA,MAAM,cAAc,MAAM;AACxB,IAAA,IAAI,OAAO,OAAA,KAAY,QAAA,IAAY,SAAA,IAAa,IAAA,EAAM;AACpD,MAAA,MAAM,kBAAA,GAAqB,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AACxC,MAAA,MAAM,mBAAmB,OAAA,GAAU,kBAAA;AAEnC,MAAA,IAAI,oBAAoB,CAAA,EAAG;AACzB,QAAA,UAAA,EAAW;AACX,QAAA;AAAA,MACF;AAEA,MAAA,OAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,gBAAgB,CAAA;AAAA,IACxC;AAEA,IAAA,OAAO,IAAA;AAAA,EACT,CAAA;AAQA,EAAA,MAAM,WAAW,MAAM;AACrB,IAAA,IAAI,aAAa,IAAA,EAAM;AACrB,MAAA,YAAA,CAAa,SAAS,CAAA;AAAA,IACxB;AAEA,IAAA,MAAM,WAAW,WAAA,EAAY;AAE7B,IAAA,SAAA,GAAY,WAAW,MAAM;AAC3B,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,UAAA,EAAW;AAAA,IACb,GAAG,QAAQ,CAAA;AAAA,EACb,CAAA;AAQA,EAAA,MAAM,cAAc,MAAM;AACxB,IAAA,IAAI,aAAa,IAAA,EAAM;AACrB,MAAA,YAAA,CAAa,SAAS,CAAA;AACtB,MAAA,SAAA,GAAY,IAAA;AAAA,IACd;AAAA,EACF,CAAA;AAKA,EAAA,MAAM,SAAS,MAAM;AACnB,IAAA,WAAA,EAAY;AACZ,IAAA,WAAA,GAAc,MAAA;AACd,IAAA,WAAA,GAAc,IAAA;AACd,IAAA,SAAA,GAAY,IAAA;AAAA,EACd,CAAA;AAKA,EAAA,MAAM,QAAQ,MAAM;AAClB,IAAA,WAAA,EAAY;AACZ,IAAA,MAAA,EAAO;AACP,IAAA,SAAA,GAAY,IAAA;AAAA,EACd,CAAA;AAEA,EAAA,MAAM,SAAA,GAAY,YAAwB,IAAA,EAAqB;AAC7D,IAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,MAAA;AAAA,IACF;AAGA,IAAA,WAAA,GAAc,IAAA;AACd,IAAA,WAAA,GAAc,IAAA;AAEd,IAAA,MAAM,cAAc,SAAA,IAAa,IAAA;AAEjC,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,SAAA,GAAY,KAAK,GAAA,EAAI;AAAA,IACvB;AAEA,IAAA,QAAA,EAAS;AAET,IAAA,IAAI,WAAW,WAAA,EAAa;AAC1B,MAAA,MAAA,EAAO;AAAA,IACT;AAAA,EACF,CAAA;AAEA,EAAA,SAAA,CAAU,MAAA,GAAS,MAAA;AACnB,EAAA,SAAA,CAAU,KAAA,GAAQ,KAAA;AAElB,EAAA,MAAA,EAAQ,iBAAiB,OAAA,EAAS,MAAA,EAAQ,EAAE,IAAA,EAAM,MAAM,CAAA;AAExD,EAAA,OAAO,SAAA;AACT;;;;"}