바닐라 큐 가이드
큐는 작업을 순서 있는 버퍼에 저장하고 개별적으로 처리합니다. 실행할 수 있는 속도보다 호출이 빠르게 들어와도 작업을 버려서는 안 되는 경우에 사용하는 Pacer의 주요 전략입니다.
큐는 용량이 있는 동안에만 손실 없이 동작합니다. 유한한 maxSize, 명시적인 지우기, 만료 또는 처리 오류로 인해 작업이 제거되거나 거부될 수 있습니다.
큐의 작동 방식
Queuing (process one item every 2 ticks)
Timeline: [1 second per tick]
Calls: ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️
Queue: [ABC] [BC] [BCDE] [DE] [E] []
Executed: ✅ ✅ ✅ ✅ ✅ ✅
[======================================================]
^ Accepted items remain queued until processed
[Items arrive] [Process steadily] [Empty]
큐는 항목 사이에 지연을 두고 자동으로 처리하거나 중지된 상태에서 수동으로 처리할 수 있습니다.
큐를 사용하는 경우
다음과 같은 경우 큐를 선택합니다.
- 수락된 모든 작업을 개별적으로 실행해야 합니다.
- 처리 순서가 중요합니다.
- 들어오는 작업이 일시적으로 처리 용량을 초과할 수 있습니다.
- 최대 버퍼 크기를 기준으로 초과 작업을 명시적으로 거부해야 합니다.
- FIFO, LIFO 또는 우선순위 정렬이 필요합니다.
다음과 같은 경우 다른 유틸리티를 선택합니다.
- 최종 호출만 중요합니다. 디바운스를 사용합니다.
- 작업이 일정하게 실행되는 동안 중간 호출을 버려도 됩니다. 스로틀을 사용합니다.
- 시간 창 할당량을 초과한 호출을 거부해야 합니다. 요청률 제한을 사용합니다.
- 항목을 함께 실행해야 합니다. 배칭을 사용합니다.
- 작업이 Promise를 반환하거나 동시에 실행되어야 합니다. 비동기 큐를 사용합니다.
TanStack Pacer에서 큐 사용하기
TanStack Pacer는 두 가지 코어 API를 제공합니다.
queue는 자동으로 실행되는 큐에 항목을 추가하는 함수를 반환합니다.Queuer는 순서 지정, 생명주기, 용량, 만료, 콜백, 상태를 노출합니다.
편의 함수
import { queue } from '@tanstack/pacer'
const processItem = queue<number>(
(item) => {
console.log('Processing:', item)
},
{ wait: 1000 },
)
processItem(1) // true, accepted and processed immediately.
processItem(2) // true, accepted and queued.
processItem(3) // true, accepted and queued.
반환된 함수는 항목이 수락되었는지를 보고합니다. 큐 생명주기 메서드는 노출하지 않습니다.
클래스 API
import { Queuer } from '@tanstack/pacer'
const queuer = new Queuer<number>(
(item) => {
console.log('Processing:', item)
},
{
wait: 1000,
maxSize: 10,
},
)
queuer.addItem(1)
queuer.addItem(2)
console.log(queuer.peekAllItems())
생성 시점에 작업이 이미 있다면 initialItems를 전달합니다. 큐는 일반적인 삽입 및 용량 규칙을 적용하며 started: false를 설정하지 않았다면 자동 처리가 즉시 시작될 수 있습니다.
결과 및 오류
동기 큐 처리기는 래핑된 함수의 반환 값을 유지하거나 오류를 포착하지 않습니다. 함수를 호출하기 전에 항목을 제거합니다. 함수에서 오류가 발생하면 해당 항목은 더 이상 큐에 있지 않으며 오류가 현재 실행에서 전파됩니다.
Promise 결과, 설정 가능한 오류 처리, 재시도, 중단 지원, 동시성이 필요하다면 비동기 큐를 사용합니다.
항목 순서 지정하기
자동 처리는 addItemsTo로 새 항목이 들어올 위치를 선택하고 getItemsFrom으로 항목이 나갈 위치를 선택합니다.
FIFO
FIFO는 가장 오래된 항목을 먼저 처리합니다. 기본값입니다.
const queuer = new Queuer(processItem, {
addItemsTo: 'back',
getItemsFrom: 'front',
started: false,
})
queuer.addItem(1)
queuer.addItem(2)
queuer.addItem(3)
queuer.start() // Processes 1, 2, 3.
LIFO
LIFO는 가장 새로운 항목을 먼저 처리합니다.
const queuer = new Queuer(processItem, {
addItemsTo: 'back',
getItemsFrom: 'back',
started: false,
})
queuer.addItem(1)
queuer.addItem(2)
queuer.addItem(3)
queuer.start() // Processes 3, 2, 1.
우선순위
숫자 우선순위가 높은 항목을 먼저 처리하려면 getPriority를 제공합니다. 우선순위 정렬은 앞이나 뒤에서 가져오는 규칙보다 우선합니다.
type Task = { name: string; priority: number }
const queuer = new Queuer<Task>(processTask, {
getPriority: (task) => task.priority,
started: false,
})
queuer.addItem({ name: 'low', priority: 1 })
queuer.addItem({ name: 'high', priority: 3 })
queuer.addItem({ name: 'medium', priority: 2 })
queuer.start() // Processes high, medium, low.
자동 및 수동 처리
기본적으로 큐는 자동으로 시작됩니다. 수락된 첫 항목은 즉시 처리되며 이후 항목 전의 지연은 wait가 제어합니다.
const queuer = new Queuer(processItem, {
wait: 1000,
})
처리 전에 항목을 수집하려면 started: false를 설정합니다.
const queuer = new Queuer(processItem, { started: false })
queuer.addItem(1)
queuer.addItem(2)
queuer.start()
queuer.stop()
stop()은 예약된 틱을 취소하고 큐에 있는 항목을 유지합니다. start()는 자동 처리를 재개합니다.
수동 제어에는 다음 메서드를 사용합니다.
execute()는 다음 항목을 제거하고 즉시 처리합니다.getNextItem()은 다음 항목을 처리하지 않고 제거하여 반환합니다.peekNextItem()은 다음 항목을 제거하지 않고 반환합니다.peekAllItems()는 현재 큐의 복사본을 반환합니다.
용량 및 거부
대기 중인 항목 수를 제한하려면 maxSize를 설정합니다. 가득 찬 큐에 추가된 항목은 거부되고 addItem()은 false를 반환하며 onReject가 실행됩니다.
const queuer = new Queuer(processItem, {
maxSize: 2,
started: false,
onReject: (item, queuer) => {
console.log('Rejected:', item)
console.log('Total rejections:', queuer.store.state.rejectionCount)
},
})
queuer.addItem(1) // true
queuer.addItem(2) // true
queuer.addItem(3) // false
활성 동기 실행은 size에 포함되지 않습니다. size는 큐에서 여전히 대기 중인 항목 수입니다.
오래된 항목 만료하기
너무 오래 대기한 항목을 제거하려면 expirationDuration을 사용합니다.
const queuer = new Queuer(processItem, {
expirationDuration: 5000,
onExpire: (item) => {
console.log('Expired:', item)
},
})
사용자 지정 로직에는 getIsExpired를 사용합니다.
const queuer = new Queuer(processItem, {
getIsExpired: (item, addedAt) => Date.now() - addedAt > item.maxAge,
})
만료는 자동 처리 루프가 실행되는 동안 확인합니다. 중지된 큐는 처리가 재개될 때 오래된 항목을 평가합니다.
플러시, 지우기 및 재설정
플러시
flush()는 설정된 지연 없이 대기 중인 항목을 즉시 처리합니다. 큐의 일부만 처리하려면 개수를 전달합니다.
queuer.flush() // Process all waiting items.
queuer.flush(2) // Process at most two waiting items.
flushAsBatch()는 대기 중인 모든 항목을 제거하여 별도의 배치 함수에 전달합니다.
queuer.flushAsBatch((items) => {
saveItems(items)
})
지우기
clear()는 대기 중인 모든 항목을 처리하지 않고 제거합니다. 큐의 실행 여부는 변경하지 않습니다.
queuer.clear()
재설정
reset()은 상태를 기본값인 실행 중인 빈 큐로 복원합니다. 이미 예약된 시간 제한은 지우지 않습니다. 예약된 작업을 취소해야 한다면 reset() 전에 stop()을 호출합니다.
queuer.stop()
queuer.reset()
큐 설정 및 관찰하기
이후 동작을 업데이트하려면 setOptions()를 사용합니다. setOptions()를 통해 started를 변경해도 start()나 stop()은 호출되지 않습니다.
queuer.setOptions({ wait: 250, maxSize: 20 })
queuer.start()
wait 옵션에는 큐 처리기 인스턴스를 받는 함수를 지정할 수 있습니다.
const queuer = new Queuer(processItem, {
wait: (queuer) => (queuer.store.state.size > 20 ? 50 : 250),
})
큐 이벤트에는 다음 콜백을 사용합니다.
onItemsChange: 항목이 추가되거나 제거되었습니다.onExecute: 항목이 처리되었습니다.onReject: 항목이 거부되었습니다.onExpire: 항목이 만료되었습니다.
타입 검사를 거친 설정을 여러 인스턴스에서 공유하려면 queuerOptions()로 정의합니다.
상태
initialState는 애플리케이션이 유지한 일부 큐 상태를 복원할 수 있습니다. items가 포함되어 있으면 initialItems보다 우선하며 initialState.isRunning도 마찬가지로 started보다 우선합니다. 지속 가능한 필드만 복원합니다. 대기 중인 타이머는 복원되지 않습니다.
일반적으로 유용한 상태는 다음과 같습니다.
items와size: 여전히 대기 중인 항목입니다.isRunning: 자동 처리가 활성화되어 있는지 나타냅니다.isIdle: 실행 중인 큐가 비어 있는지 나타냅니다.isFull:maxSize에 도달했는지 나타냅니다.executionCount: 래핑된 함수가 성공적으로 반환된 항목 수입니다.rejectionCount와expirationCount: 처리되지 않고 제거된 항목 수입니다.status:'idle','running'또는'stopped'입니다.
전체 옵션과 상태 타입은 Queuer API 레퍼런스를 참고합니다.