본문으로 건너뛰기

함수: useAsyncQueuer()

function useAsyncQueuer<TValue, TSelected>(
fn,
options,
selector): ReactAsyncQueuer<TValue, TSelected>;

정의 위치: react-pacer/src/async-queuer/useAsyncQueuer.ts:235

항목의 비동기 큐를 관리하는 AsyncQueuer 인스턴스를 생성하는 하위 수준 React 훅입니다.

기능:

  • getPriority 옵션을 통한 우선순위 큐 지원
  • 구성 가능한 동시 실행 한도
  • 작업 성공/오류/완료 콜백
  • FIFO(First In First Out) 또는 LIFO(Last In First Out) 큐 동작
  • 작업 처리 일시 중지/재개
  • 작업 취소
  • 큐에서 오래된 항목을 제거하는 항목 만료

작업은 구성된 동시 실행 한도까지 동시에 처리됩니다. 작업이 완료되면 현재 실행 수가 동시 실행 한도보다 적을 경우 다음 대기 작업을 처리합니다.

오류 처리:

  • onError 핸들러를 제공하면 오류 및 큐어 인스턴스와 함께 호출됩니다.
  • throwOnError가 true이면(onError 핸들러가 없을 때의 기본값) 오류가 발생합니다.
  • throwOnError가 false이면(onError 핸들러가 있을 때의 기본값) 오류가 처리된 것으로 간주됩니다.
  • onError와 throwOnError를 함께 사용할 수 있으며, 오류가 발생하기 전에 핸들러가 호출됩니다.
  • 내부 AsyncQueuer 인스턴스를 사용해 오류 상태를 확인할 수 있습니다.

상태 관리와 셀렉터

훅은 반응형 상태 관리에 TanStack Store를 사용합니다. 다음 두 가지 방식으로 상태 변경을 구독할 수 있습니다.

1. queuer.Subscribe HOC 사용(컴포넌트 트리 구독에 권장)

Subscribe HOC를 사용하면 컴포넌트 트리 깊은 곳의 상태 변경을 구독할 때 훅에 셀렉터를 전달할 필요가 없습니다. 자식 컴포넌트에서 상태를 구독하려는 경우에 적합합니다.

2. selector 매개변수 사용(훅 수준 구독)

selector 매개변수를 사용하면 어떤 상태 변경이 재렌더링을 트리거할지 지정할 수 있으며 관련 없는 상태가 변경될 때 불필요한 재렌더링을 방지하여 훅 수준의 성능을 최적화합니다.

기본적으로 반응형 상태 구독은 없습니다. 상태 추적을 명시적으로 활성화하려면 셀렉터 함수를 제공하거나 Subscribe HOC를 사용해야 합니다. 이렇게 하면 불필요한 재렌더링을 방지하고 컴포넌트가 업데이트되는 시점을 완전히 제어할 수 있습니다.

사용 가능한 상태 속성:

  • activeItems: 큐어가 현재 처리 중인 항목
  • errorCount: 오류가 발생한 작업 실행 횟수
  • expirationCount: 만료로 제거된 항목 수
  • isEmpty: 큐어에 처리할 항목이 없는지 여부
  • isFull: 큐어가 최대 용량에 도달했는지 여부
  • isIdle: 큐어가 현재 어떤 항목도 처리하지 않는지 여부
  • isRunning: 큐어가 활성 상태이며 항목을 자동으로 처리할지 여부
  • items: 현재 처리 대기 중인 항목 배열
  • itemTimestamps: 만료 추적을 위해 항목이 추가된 시점의 타임스탬프
  • lastResult: 가장 최근 작업 실행 결과
  • pendingTick: 큐어에 다음 항목 처리를 위한 대기 중 타임아웃이 있는지 여부
  • rejectionCount: 추가가 거부된 항목 수
  • settledCount: 성공 또는 오류로 완료된 작업 실행 횟수
  • size: 현재 큐에 있는 항목 수
  • status: 현재 처리 상태('idle' | 'running' | 'stopped')
  • successCount: 성공적으로 완료된 작업 실행 횟수

언마운트 동작

기본적으로 컴포넌트가 언마운트되면 훅은 큐어를 중지하고 진행 중인 작업 실행을 중단합니다. getAbortSignal()의 중단 신호를 내부 작업(예: fetch)에 전달한 경우에만 Abort가 해당 작업을 취소합니다. 이를 사용자 지정하려면 onUnmount 옵션을 사용합니다. 예를 들어 대기 중인 항목을 대신 플러시하려면 다음과 같이 합니다.

const queuer = useAsyncQueuer(fn, {
concurrency: 2,
started: false,
onUnmount: (q) => q.flush()
});

참고: 비동기 유틸리티에서 flush()는 Promise를 반환하며 정리 과정에서는 실행 후 결과를 기다리지 않습니다. 작업 함수가 React 상태를 업데이트하면 해당 업데이트가 컴포넌트가 언마운트된 뒤 실행될 수 있으며, 이 경우 "setState on unmounted component" 경고가 발생할 수 있습니다. 콜백을 onUnmount에서 flush와 함께 사용할 때 적절히 보호해야 합니다.

타입 매개변수

TValue

TValue

TSelected

TSelected = { }

매개변수

fn

(value) => Promise&lt;any>

options

ReactAsyncQueuerOptions&lt;TValue, TSelected> = {}

selector

(state) => TSelected

반환값

ReactAsyncQueuer&lt;TValue, TSelected>

예시

// Default behavior - no reactive state subscriptions
const asyncQueuer = useAsyncQueuer(
async (item) => {
const result = await processItem(item);
return result;
},
{ concurrency: 2, maxSize: 100, started: false }
);

// Subscribe to state changes deep in component tree using Subscribe HOC
<asyncQueuer.Subscribe selector={(state) => ({ size: state.size, isRunning: state.isRunning })}>
{({ size, isRunning }) => (
<div>Queue: {size} items, {isRunning ? 'Processing' : 'Idle'}</div>
)}
</asyncQueuer.Subscribe>

// Opt-in to re-render when queue size changes at hook level (optimized for displaying queue length)
const asyncQueuer = useAsyncQueuer(
async (item) => {
const result = await processItem(item);
return result;
},
{ concurrency: 2, maxSize: 100, started: false },
(state) => ({
size: state.size,
isEmpty: state.isEmpty,
isFull: state.isFull
})
);

// Opt-in to re-render when processing state changes (optimized for loading indicators)
const asyncQueuer = useAsyncQueuer(
async (item) => {
const result = await processItem(item);
return result;
},
{ concurrency: 2, maxSize: 100, started: false },
(state) => ({
isRunning: state.isRunning,
isIdle: state.isIdle,
status: state.status,
activeItems: state.activeItems,
pendingTick: state.pendingTick
})
);

// Opt-in to re-render when execution metrics change (optimized for stats display)
const asyncQueuer = useAsyncQueuer(
async (item) => {
const result = await processItem(item);
return result;
},
{ concurrency: 2, maxSize: 100, started: false },
(state) => ({
successCount: state.successCount,
errorCount: state.errorCount,
settledCount: state.settledCount,
expirationCount: state.expirationCount,
rejectionCount: state.rejectionCount
})
);

// Opt-in to re-render when results are available (optimized for data display)
const asyncQueuer = useAsyncQueuer(
async (item) => {
const result = await processItem(item);
return result;
},
{
concurrency: 2,
maxSize: 100,
started: false,
onSuccess: (result) => {
console.log('Item processed:', result);
},
onError: (error) => {
console.error('Processing failed:', error);
}
},
(state) => ({
lastResult: state.lastResult,
successCount: state.successCount
})
);

// Add items to queue
asyncQueuer.addItem(newItem);

// Start processing
asyncQueuer.start();

// Access the selected state (will be empty object {} unless selector provided)
const { size, isRunning, activeItems } = asyncQueuer.state;