본문으로 건너뛰기

Solid 비동기 배칭 가이드

비동기 배칭은 배칭 가이드에서 설명한 수집 및 트리거 동작을 유지하면서 Promise 결과, 재시도, 오류 콜백, 실패 항목 추적, 진행 중인 작업에 대한 제어 기능을 추가합니다.

하나의 비동기 작업으로 수집한 여러 항목을 함께 처리해야 할 때 사용합니다. 각 항목을 개별적으로 실행해야 하거나 동시 실행 수를 제한해야 한다면 비동기 큐를 사용합니다.

비동기 배칭의 작동 방식​

설정된 트리거 중 하나가 작동할 때까지 항목을 수집합니다.

add A ─── add B ─── add C
│ │ │
└─ wait reset └─ maxSize reached
│
└─ execute [A, B, C]

다음과 같은 경우 배치를 실행합니다.

  • 길이가 maxSize에 도달하는 경우
  • getShouldExecute(items, batcher)가 true를 반환하는 경우
  • wait밀리초 동안 새 항목이 추가되지 않는 경우

maxSize와 wait의 기본값은 모두 Infinity이므로 트리거를 하나 이상 설정하거나 flush()를 직접 호출해야 합니다. 대기 타이머는 항목을 추가할 때마다 다시 시작합니다. 이 타이머는 유휴 시간을 측정하며, 가장 오래된 항목의 최대 보관 시간을 측정하지 않습니다.

API 선택하기​

  • 항목 수집, 플러시, 실패 처리, 선택된 상태에는 createAsyncBatcher를 사용합니다.

Solid 예시​

import { createAsyncBatcher } from '@tanstack/solid-pacer'

const batcher = createAsyncBatcher(
sendEvents,
{ maxSize: 20, wait: 1000 },
(state) => ({
size: state.size,
isExecuting: state.isExecuting,
}),
)

void batcher.addItem({ type: 'click' })
console.log(batcher.state().size)

이 가이드의 이후 핵심 코드 조각은 createAsyncBatcher을 사용하며 Solid 반응형 소유자 안에서 실행된다고 가정합니다.

Promise 결과​

flush()는 배치 함수의 결과를 반환하며 특정 배치를 기다리는 가장 명확한 방법입니다.

addItem()도 Promise를 반환하지만 개별 항목의 결과를 받는 수단으로 간주해서는 안 됩니다.

  • 해당 항목 추가로 maxSize에 도달하거나 getShouldExecute를 충족하면, 반환된 Promise가 트리거된 실행을 담당하며 배치 결과로 이행됩니다.
  • 대기 타이머만 예약하면 반환된 Promise는 이후의 배치 결과 없이 이행됩니다.
  • 다른 항목을 추가하면 타이머가 재설정되고 함께 실행되는 항목이 달라질 수 있습니다.

모든 항목 추가에서 최종 배치 결과를 확인해야 한다면 onSuccess, onError, onSettled를 사용합니다.

배치 경계와 중첩 작업​

배처는 비동기 함수를 호출하기 전에 현재 항목을 복사하고 비웁니다. 해당 함수가 실행 중일 때 추가한 항목은 새 배치에 수집됩니다.

execute [A, B] ───────────────── finish
add C ─── add D ─── execute [C, D] ─── finish

첫 번째 배치가 끝나기 전에 두 번째 배치의 트리거가 작동하면 두 배치 함수가 겹쳐 실행될 수 있습니다. createAsyncBatcher에는 동시성 옵션이 없습니다. 중첩 실행이 안전하지 않다면 배처 외부에서 배치 실행을 직렬화하거나 완료된 배치를 비동기 큐로 전달합니다.

오류와 실패 항목​

비동기 배처는 다음 콜백을 제공합니다.

  • 성공한 후에는 onSuccess(result, batch, batcher)를 호출합니다.
  • 배치 재시도가 실패한 후에는 onError(error, batch, batcher)를 호출합니다.
  • 어느 결과든 완료된 후에는 onSettled(batch, batcher)를 호출합니다.
  • 항목을 추가하거나 실행을 위해 제거할 때는 onItemsChange(batcher)를 호출합니다.

onError가 없으면 throwOnError의 기본값이 true이므로 실패 시 flush() 또는 크기 기준 실행을 트리거한 addItem()이 거부됩니다. onError를 제공하면 이 기본값이 false로 바뀌고 Promise는 undefined로 이행됩니다.

항목은 실행 전에 대기 중인 컬렉션에서 제거됩니다. 실패한 배치는 자동으로 큐에 다시 추가되지 않습니다. 해당 항목은 failedItems에 추가되며 peekFailedItems()로 확인할 수 있습니다. clear()나 이후 실행이 이 컬렉션을 비울 때까지 유지됩니다.

const failed = batcher.peekFailedItems()
for (const item of failed) {
saveForManualRecovery(item)
}

멱등성이 없는 작업은 실패한 배치를 다시 제출하기 전에 서버의 처리 결과를 확인합니다.

배치 재시도하기​

asyncRetryerOptions로 각 배치 실행의 재시도를 설정합니다.

const batcher = createAsyncBatcher(sendEvents, {
maxSize: 20,
wait: 1000,
asyncRetryerOptions: {
maxAttempts: 3,
backoff: 'exponential',
baseWait: 500,
jitter: 0.2,
},
})

maxAttempts에는 첫 번째 시도가 포함되며 모든 재시도에는 복사된 동일한 배치가 전달됩니다. 부수 효과가 있는 작업을 재시도하기 전에 비동기 재시도 가이드를 참고합니다.

플러시, 취소, 비우기​

  • flush()는 대기 중인 타이머를 지우고 현재 항목을 즉시 실행합니다.
  • cancel()은 대기 중인 타이머를 지우지만 수집한 항목은 유지합니다.
  • clear()는 수집한 항목과 실패한 항목을 제거하지만 예약된 타이머는 지우지 않습니다.
  • peekAllItems()는 현재 수집한 항목의 복사본을 반환합니다.

clear()는 타이머를 그대로 두므로 빈 타이머가 남지 않아야 한다면 cancel()에 이어 clear()를 사용합니다.

batcher.cancel()
batcher.clear()

나중에 타이머 또는 flush()가 항목을 찾지 못하면 배치 함수를 호출하지 않습니다.

활성 작업 중단하기​

abort()는 활성 재시도기를 중단합니다. 대기 중인 배치를 취소하거나 수집한 항목을 제거하지는 않습니다. 취소가 전파되도록 배처의 시그널을 기반 API에 전달합니다.

const batcher = createAsyncBatcher(
async (events: Array<AnalyticsEvent>) => {
return fetch('/api/analytics/batch', {
method: 'POST',
body: JSON.stringify(events),
signal: batcher.getAbortSignal() ?? undefined,
})
},
{ maxSize: 20, wait: 1000 },
)

batcher.abort()

실행이 겹칠 때 특정 실행의 시그널이 필요하면 해당 실행의 executeCount를 getAbortSignal()에 전달합니다.

안전하게 재설정하기​

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

batcher.cancel()
batcher.abort()
batcher.reset()

Solid 수명 주기​

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

설정과 반응형 상태​

어댑터는 셀렉터 인수가 반환한 상태만 구독합니다. 셀렉터가 없으면 어댑터 상태는 비어 있습니다. Solid 반응형 소유자 안에서 유틸리티를 생성하고 뷰에서 사용하는 필드만 선택합니다.

const batcher = createAsyncBatcher(
sendEvents,
{ maxSize: 20, wait: 1000 },
(state) => ({
size: state.size,
isExecuting: state.isExecuting,
failedItems: state.failedItems,
}),
)

console.log(
batcher.state().size,
batcher.state().isExecuting,
batcher.state().failedItems,
)

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

wait에는 숫자 또는 배처 인스턴스를 받는 함수를 사용할 수 있습니다. setOptions()는 새 옵션을 병합하고 asyncBatcherOptions()는 재사용 가능한 타입 검사 옵션 객체를 만듭니다.

배처를 일시 중지하는 데 started를 사용하지 않습니다. 현재 아무 동작도 하지 않으므로 모든 addItem() 호출에서 설정된 트리거를 평가합니다.

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

일반적인 상태에는 다음 항목이 포함됩니다.

  • items, size, isPending: 다음 배치와 그 타이머 상태입니다.
  • isExecuting: 배치를 실행 중인 것으로 보고하는지 여부입니다.
  • lastResult: 가장 최근의 성공 결과입니다.
  • failedItems, totalItemsFailed: 실패 추적 정보입니다.
  • successCount, errorCount, settleCount: 배치 결과 횟수입니다.
  • totalItemsProcessed: 성공한 배치 실행의 항목 수입니다.

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