본문으로 건너뛰기

바닐라 비동기 배칭 가이드

비동기 배칭은 배칭 가이드에서 설명한 수집 및 트리거 동작을 유지하면서 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()를 직접 호출합니다. 항목을 추가할 때마다 대기 타이머가 다시 시작됩니다. 이 타이머는 가장 오래된 항목의 최대 수명이 아니라 유휴 기간을 측정합니다.

빠른 시작​

항목 추가 작업만 필요하다면 asyncBatch를 사용합니다.

import { asyncBatch } from '@tanstack/pacer'

const addAnalyticsEvent = asyncBatch(
async (events: Array<AnalyticsEvent>) => {
const response = await fetch('/api/analytics/batch', {
method: 'POST',
body: JSON.stringify(events),
})
if (!response.ok) throw new Error('Batch failed')
return response.json()
},
{ maxSize: 20, wait: 1000 },
)

addAnalyticsEvent(event)

생명주기 메서드, 콜백, 상태가 필요하다면 AsyncBatcher를 사용합니다.

import { AsyncBatcher } from '@tanstack/pacer'

const batcher = new AsyncBatcher(sendEvents, {
maxSize: 20,
wait: 1000,
onSuccess: (result, batch) => {
console.log('Sent:', batch.length, result)
},
onError: (error, batch) => {
console.error('Failed batch:', batch, error)
},
})

batcher.addItem(event)
const result = await batcher.flush()

Promise 결과​

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

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

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

모든 추가 작업에서 최종 배치 결과를 관찰해야 한다면 onSuccess, onError, onSettled를 사용합니다.

배치 경계와 겹치는 작업​

배처는 비동기 함수를 호출하기 전에 현재 항목을 복사하고 지웁니다. 해당 함수가 활성 상태인 동안 추가된 항목은 새 배치에 수집됩니다.

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

첫 배치가 끝나기 전에 두 번째 배치의 트리거가 발생하면 두 배치 함수가 겹칠 수 있습니다. AsyncBatcher에는 동시성 옵션이 없습니다. 실행이 겹치면 안전하지 않은 경우 배처 외부에서 배치 실행을 직렬화하거나 완성된 배치를 비동기 큐를 통해 전달합니다.

오류 및 실패한 항목​

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

  • onSuccess(result, batch, batcher)는 성공 후 실행됩니다.
  • onError(error, batch, batcher)는 배치의 재시도가 실패한 후 실행됩니다.
  • onSettled(batch, batcher)는 어느 결과든 완료된 후 실행됩니다.
  • onItemsChange(batcher)는 항목이 추가되거나 실행을 위해 제거될 때 실행됩니다.

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

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

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

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

배치 재시도​

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

const batcher = new AsyncBatcher(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 = new AsyncBatcher(
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()

설정 및 상태​

wait에는 숫자 또는 배처 인스턴스를 받는 함수를 지정할 수 있습니다. setOptions()는 새 옵션을 병합하고 asyncBatcherOptions()는 재사용할 수 있고 타입 검사를 거친 옵션 객체를 생성합니다.

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

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

일반적인 상태는 다음과 같습니다.

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

전체 옵션과 상태 타입은 AsyncBatcher API 레퍼런스를 참고합니다.