바닐라 디바운스 가이드
디바운스는 설정된 시간 동안 호출이 멈출 때까지 함수 실행을 지연합니다. 새 호출이 들어올 때마다 타이머가 다시 시작됩니다. 기본 설정에서는 가장 최근 호출만 해당 인수를 사용하여 실행됩니다.
중간 호출을 버릴 수 있고 최종 값이 중요할 때 디바운스를 사용합니다. 검색 입력, 폼 검증, 자동 저장, 크기 조절 처리가 일반적인 예입니다.
디바운스의 작동 방식
다음 타임라인은 호출이 버스트로 들어오는 모습을 보여 줍니다. 모든 호출이 타이머를 재설정합니다. 각 버스트의 최종 호출은 활동이 없는 상태로 3틱이 지난 후 실행됩니다.
Debouncing (wait: 3 ticks)
Timeline: [1 second per tick]
Calls: ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️
Executed: ❌ ❌ ❌ ❌ ❌ ❌ ❌ ❌ ⏳ -> ✅ ❌ ⏳ -> ✅
[================================================================]
^ Executes here after
3 ticks of no calls
[Burst of calls] [More calls] [Wait] [New burst]
No execution Resets timer Execute Reset and execute
각 버스트에서 가장 최근 호출만 실행됩니다. 이전 호출은 모두 버려집니다.
디바운스는 의도적으로 일부 호출을 손실시킵니다. 모든 작업을 반드시 실행해야 한다면 대신 큐를 사용합니다.
디바운스를 사용하는 경우
다음과 같은 경우 디바운스를 선택합니다.
- 활동이 멈출 때까지 기다려야 합니다.
- 가장 최근 인수만 중요합니다.
- 모든 이벤트에 작업을 반복하면 자원이 낭비됩니다.
- 짧은 지연을 허용할 수 있습니다.
다음과 같은 경우 다른 유틸리티를 선택합니다.
- 활동이 계속되는 동안 작업을 일정한 간격으로 실행해야 합니다. 스로틀을 사용합니다.
- 시간 창 안에서 정해진 수의 호출을 실행할 수 있어야 합니다. 요청률 제한을 사용합니다.
- 모든 작업을 최종적으로 실행해야 합니다. 큐를 사용합니다.
- 여러 항목을 함께 처리해야 합니다. 배칭을 사용합니다.
- 결과를 기다리거나 오류를 처리하거나 재시도하거나 진행 중인 작업을 중단해야 합니다. 비동기 디바운스를 사용합니다.
TanStack Pacer에서 디바운스 사용하기
TanStack Pacer는 두 가지 코어 디바운스 API를 제공합니다.
debounce는 디바운스된 함수를 반환합니다.Debouncer는 디바운스된 함수와 함께 생명주기 메서드 및 관찰 가능한 상태를 노출합니다.
편의 함수
디바운스된 함수를 호출하기만 하면 된다면 debounce를 사용합니다.
import { debounce } from '@tanstack/pacer'
const search = debounce(
(query: string) => {
updateSearchResults(query)
},
{ wait: 500 },
)
search('t')
search('ta')
search('tanstack')
// After 500ms without another call:
// updateSearchResults('tanstack')
반환된 함수는 cancel()이나 flush() 같은 메서드를 노출하지 않습니다. 이러한 제어가 필요하다면 클래스 API를 사용합니다.
클래스 API
생명주기 메서드, 동적 옵션, 콜백 또는 상태가 필요하다면 Debouncer를 사용합니다.
import { Debouncer } from '@tanstack/pacer'
const searchDebouncer = new Debouncer(
(query: string) => {
updateSearchResults(query)
},
{ wait: 500 },
)
searchDebouncer.maybeExecute('tanstack')
console.log(searchDebouncer.store.state.isPending) // true
// Execute the pending call now instead of waiting.
searchDebouncer.flush()
debounce가 반환한 함수와 Debouncer.maybeExecute()는 모두 void를 반환합니다. 호출자가 래핑된 함수의 결과를 기다려야 한다면 AsyncDebouncer를 사용합니다.
결과 및 오류
동기 디바운서는 래핑된 함수의 반환 값을 유지하거나 오류를 포착하지 않습니다. 선행 실행의 오류는 maybeExecute()에서 전파됩니다. 후행 실행은 나중에 타이머에서 실행되므로 이전 maybeExecute() 호출 주위에서는 해당 오류를 포착할 수 없습니다.
동기 오류는 래핑된 함수 안에서 처리합니다. Promise 결과와 설정 가능한 비동기 오류 처리에는 비동기 디바운스를 사용합니다.
실행 타이밍
leading과 trailing 옵션은 대기 기간의 어느 에지에서 실행할 수 있는지를 제어합니다.
leading | trailing | 동작 |
|---|---|---|
false | true | 활동이 멈출 때까지 기다린 다음 가장 최근 호출을 실행합니다. 기본값입니다. |
true | false | 첫 호출을 즉시 실행합니다. 이후 호출은 실행되지 않고 대기 기간을 다시 시작합니다. |
true | true | 첫 호출을 즉시 실행합니다. 대기 기간 중에 다른 호출이 들어오면 가장 최근 호출을 후행 에지에서 실행합니다. |
false | false | 어떤 호출도 실행하지 않습니다. |
const debouncer = new Debouncer(saveDraft, {
wait: 1000,
leading: true,
trailing: true,
})
debouncer.maybeExecute('first') // Executes immediately.
debouncer.maybeExecute('second')
debouncer.maybeExecute('latest') // Executes after 1 second of inactivity.
두 에지를 모두 활성화하면 단일 호출은 선행 에지에서만 실행됩니다. 후행 실행은 대기 기간 중에 다른 호출이 들어오는 경우에만 발생합니다.
최대 대기 시간 없음
Debouncer는 maxWait 옵션을 제공하지 않습니다. 호출이 계속 들어오면 후행 실행이 무기한 연기될 수 있습니다. 호출이 계속 들어오는 동안에도 작업을 제한된 간격으로 계속해야 한다면 스로틀을 사용합니다.
대기 중인 작업 제어하기
클래스 API는 대기 중인 작업의 실행, 취소, 재설정을 구분합니다.
플러시
flush()는 대기 중인 후행 호출을 가장 최근 인수로 즉시 실행합니다. 대기 중인 후행 호출이 없으면 아무 작업도 하지 않습니다.
const debouncer = new Debouncer(saveDraft, { wait: 1000 })
debouncer.maybeExecute('draft')
debouncer.flush() // Executes saveDraft('draft') now.
취소
cancel()은 함수를 실행하지 않고 대기 중인 시간 제한을 지웁니다. 또한 다음에 maybeExecute()가 호출될 때 선행 호출을 즉시 실행할 수 있게 합니다.
debouncer.maybeExecute('discarded draft')
debouncer.cancel()
재설정
reset()은 디바운서의 상태 카운터와 플래그를 기본값으로 복원합니다. 이미 예약된 시간 제한은 지우지 않습니다. 대기 중인 작업을 버리고 상태를 재설정해야 한다면 먼저 cancel()을 호출합니다.
debouncer.cancel()
debouncer.reset()
런타임에 동작 설정하기
생성 후 옵션을 변경하려면 setOptions()를 사용합니다.
debouncer.setOptions({
wait: 1000,
leading: true,
trailing: false,
})
새 wait 값은 다음 호출에서 시간 제한을 예약할 때 적용됩니다. 이미 대기 중인 시간 제한의 일정은 다시 잡지 않습니다. maybeExecute()를 다시 호출하면 이전 시간 제한을 지우고 현재 옵션을 사용하여 새 시간 제한을 예약합니다.
활성화 및 비활성화
실행을 막으려면 enabled를 false로 설정합니다. setOptions()를 통해 디바운서를 비활성화하면 대기 중인 호출도 취소됩니다.
const debouncer = new Debouncer(saveDraft, {
wait: 500,
enabled: false,
})
debouncer.maybeExecute('ignored')
debouncer.setOptions({ enabled: true })
debouncer.maybeExecute('saved')
enabled와 wait 옵션에는 디바운서 인스턴스를 받는 함수를 지정할 수도 있습니다.
const debouncer = new Debouncer(saveDraft, {
enabled: (debouncer) => debouncer.store.state.executionCount < 10,
wait: (debouncer) =>
debouncer.store.state.executionCount === 0 ? 300 : 500,
})
실행 관찰하기
래핑된 함수가 실행된 후 부수 효과를 수행하려면 onExecute를 사용합니다. 콜백은 실행된 인수와 디바운서 인스턴스를 차례로 받습니다.
const debouncer = new Debouncer(saveDraft, {
wait: 500,
onExecute: (args, debouncer) => {
console.log('Saved arguments:', args)
console.log('Execution count:', debouncer.store.state.executionCount)
},
})
타입 검사를 거친 설정을 여러 인스턴스에서 공유하려면 debouncerOptions()로 정의합니다.
상태
클래스는 debouncer.store의 TanStack Store에 상태를 저장합니다.
애플리케이션이 유지한 일부 상태를 복원하려면 부분 스냅샷을 initialState로 전달합니다. 이 스냅샷은 기본값과 병합됩니다. 지속 가능한 필드만 복원합니다. 대기 중인 타이머는 복원되지 않습니다.
애플리케이션 코드에서 가장 많이 사용하는 속성은 다음과 같습니다.
isPending: 후행 실행이 대기 중인지 나타냅니다.executionCount: 래핑된 함수가 실행된 횟수입니다.lastArgs: 후행 실행이 활성화된 가장 최근 호출에서 기록된 인수입니다. 대기 중인 작업으로 취급하기 전에isPending을 확인합니다.status:'disabled','idle'또는'pending'입니다.
const unsubscribe = debouncer.store.subscribe((state) => {
console.log(state.isPending, state.executionCount)
})
unsubscribe()
전체 상태와 옵션 타입은 Debouncer API 레퍼런스를 참고합니다.