본문으로 건너뛰기

Preact 비동기 디바운싱 가이드

비동기 디바운싱은 디바운싱 가이드에서 설명한 타이밍 동작을 유지하면서 Promise 결과, 재시도, 오류 콜백, 진행 중인 작업에 대한 제어 기능을 추가합니다.

디바운스된 작업이 필요한 값을 반환하거나 거부될 수 있을 때, 또는 재시도와 중단 지원이 필요할 때 비동기 디바운싱을 사용합니다. 동기 디바운싱 어댑터도 부수 효과로 비동기 함수를 호출할 수 있지만 결과 Promise를 관리하지는 않습니다.

API 선택하기

  • 안정적인 Promise 반환 핸들러가 필요하면 useAsyncDebouncedCallback
  • 수명 주기 메서드와 선택한 실행 상태가 필요하면 useAsyncDebouncer

Preact 예제

import { useAsyncDebouncedCallback } from '@tanstack/preact-pacer'

function SearchBox() {
const search = useAsyncDebouncedCallback(fetchSearchResults, {
wait: 300,
onError: reportError,
})

return <input onChange={(event) => void search(event.currentTarget.value)} />
}

이 가이드에서 이후에 다루는 핵심 코드 조각은 useAsyncDebouncer를 사용하며 컴포넌트나 다른 훅 내부에서 실행한다고 가정합니다.

Promise 결과

maybeExecute()는 Promise를 반환합니다. 실행을 담당하는 호출은 해당 실행 결과로 이행됩니다. 대기 중인 후행 호출이 대체될 때 중요한 결과가 하나 있습니다.

call A ──────┐
├─ call B replaces A ───── wait ───── execute B
Promise A ───┘ resolves with the previous lastResult
Promise B ─────────────────────────────── resolves with result B

대체된 호출은 디바운서의 현재 lastResult로 즉시 이행되며, 첫 번째 실행이 성공하기 전에는 대개 undefined입니다. 새 호출을 기다리지 않습니다. 가장 최근 호출이 반환한 Promise가 대기 중인 결과를 담당한다고 간주합니다.

모든 호출을 실행하고 각 호출의 결과를 생성해야 한다면 비동기 큐를 사용합니다.

선행 및 후행 실행

네 가지 조합은 동기 디바운싱과 동일합니다.

leadingtrailing동작
falsetrue호출이 wait밀리초 동안 멈춘 후 실행합니다. 기본 동작입니다.
truefalse즉시 실행한 다음 유휴 시간이 끝날 때까지 호출을 무시합니다.
truetrue첫 호출은 즉시 실행하고 이후의 가장 최근 호출은 후행 시점에 실행합니다.
falsefalse함수를 실행하지 않고 호출을 기록합니다.

두 시점을 모두 활성화하면 단일 호출은 선행 시점에만 실행됩니다. 후행 실행이 발생하려면 대기 시간 중에 다른 호출이 있어야 합니다.

오류와 콜백

비동기 디바운서는 각 실제 실행 전후에 다음 콜백을 제공합니다.

  • onSuccess(result, args, debouncer)는 실행이 성공한 후 호출됩니다.
  • onError(error, args, debouncer)는 실행 재시도가 실패한 후 호출됩니다.
  • onSettled(args, debouncer)는 어느 결과든 완료된 후 호출됩니다.

onError가 없으면 throwOnError의 기본값이 true이므로 실행 실패 시 Promise가 거부됩니다. onError를 제공하면 이 기본값이 false로 바뀌고 Promise는 현재 lastResult로 이행됩니다. 다른 동작이 필요하면 throwOnError를 명시적으로 설정합니다.

콜백은 실행마다 호출되며 maybeExecute()를 호출할 때마다 호출되지는 않습니다. 대체되거나 취소된 대기 호출은 래핑된 함수에 전달되지 않습니다.

실패한 실행 재시도하기

실행이 시작된 후 재시도하려면 asyncRetryerOptions를 전달합니다.

const save = useAsyncDebouncer(saveDraft, {
wait: 500,
asyncRetryerOptions: {
maxAttempts: 3,
backoff: 'exponential',
baseWait: 500,
jitter: 0.2,
},
})

maxAttempts에는 첫 번째 시도가 포함됩니다. 디바운싱은 하나의 논리적 실행이 시작되는 시점을 결정하며, 그다음 재시도기가 해당 실행의 시도를 관리합니다. 재시도 안전성, 백오프, 타임아웃 동작은 비동기 재시도 가이드를 참고합니다.

대기 작업 취소 및 활성 작업 중단하기

대기 작업과 활성 작업은 별도로 제어합니다.

  • cancel()은 아직 시작하지 않은 후행 실행을 지웁니다. 활성 Promise는 중단하지 않습니다.
  • abort()는 활성 실행을 중단합니다. 대기 중인 후행 실행은 지우지 않습니다.
  • flush()는 대기 중인 작업을 즉시 시작하고 그 결과를 반환합니다. 활성 작업에는 영향을 주지 않습니다.

fetch 같은 기반 작업을 중단하려면 디바운서의 시그널을 전달합니다.

const search = useAsyncDebouncer(
async (query: string) => {
const response = await fetch(`/api/search?q=${query}`, {
signal: search.getAbortSignal() ?? undefined,
})
return response.json()
},
{ wait: 300 },
)

search.maybeExecute('pacer')
search.abort()

시그널을 사용하지 않고 abort()를 호출하면 재시도 관리는 중단되지만 임의의 Promise를 강제로 중단할 수는 없습니다.

안전하게 재설정하기

reset()은 기본 상태를 복원하지만 예약된 후행 타임아웃을 지우거나 활성 작업의 중단을 보장하지 않습니다. 완전히 정리해야 한다면 먼저 수명 주기 메서드를 사용합니다.

search.cancel()
search.abort()
search.reset()

설정

waitenabled에는 값 또는 디바운서 인스턴스를 받는 함수를 사용할 수 있습니다. setOptions()는 새 옵션을 현재 설정에 병합합니다.

search.setOptions({
enabled: (debouncer) => debouncer.store.state.errorCount < 3,
wait: (debouncer) => (debouncer.store.state.successCount === 0 ? 200 : 500),
})

wait를 변경해도 기존 타임아웃을 다시 예약하지 않습니다. 새 값은 이후 작업을 예약할 때 적용됩니다.

재사용 가능한 타입 검사 옵션 객체를 정의하려면 asyncDebouncerOptions()를 사용합니다.

Preact 수명 주기

어댑터는 소유자가 제거될 때 대기 작업을 취소하고 활성 작업을 중단합니다. onUnmount를 제공하면 이 기본 정리 동작을 대체하므로 사용자 정의 콜백에서 필요한 모든 수명 주기 작업을 수행해야 합니다. 사용자 정의 정리 과정에서 작업을 플러시하면 컴포넌트가 제거되는 동안 사용자 콜백이 실행될 수 있다는 점에 유의해야 합니다.

반응형 상태

어댑터는 셀렉터 인수가 반환한 상태만 구독합니다. 셀렉터가 없으면 어댑터 상태는 비어 있습니다. 컴포넌트나 다른 훅의 최상위 수준에서 유틸리티를 만들고 뷰에서 사용하는 필드만 선택합니다.

const debouncer = useAsyncDebouncer(
fetchSearchResults,
{ wait: 300 },
(state) => ({
isPending: state.isPending,
isExecuting: state.isExecuting,
lastResult: state.lastResult,
}),
)

console.log(
debouncer.state.isPending,
debouncer.state.isExecuting,
debouncer.state.lastResult,
)

옵션 함수와 수명 주기 콜백은 기반 공개 유틸리티 인스턴스를 받습니다. 위 예제처럼 해당 콜백 안에서 .store.state를 읽는 방식을 지원합니다. 렌더링 코드는 여기에 나온 선택된 어댑터 상태를 읽어야 합니다.

앱에서 유지한 선택 상태를 복원하려면 initialState로 부분 스냅샷을 전달합니다. 이 스냅샷은 기본값과 병합됩니다. 지속 가능한 필드만 복원합니다. 대기 중인 타이머와 활성 실행은 복원되지 않습니다.

  • isPending: 후행 실행이 예약되어 있는지 여부입니다.
  • isExecuting: 래핑된 함수가 활성 상태인지 여부입니다.
  • lastArgs: 대기 작업을 위해 보존한 인수입니다.
  • lastResult: 가장 최근의 성공 결과입니다.
  • successCount, errorCount, settleCount: 실행 결과 횟수입니다.
  • status: 'disabled', 'idle', 'pending', 'executing', 'settled' 중 하나입니다.

어댑터 시그니처는 Preact API 레퍼런스를, 전체 옵션 및 상태 타입은 공개 코어 레퍼런스를 참고합니다.