본문으로 건너뛰기

바닐라 큐 가이드

큐는 작업을 순서 있는 버퍼에 저장하고 개별적으로 처리합니다. 실행할 수 있는 속도보다 호출이 빠르게 들어와도 작업을 버려서는 안 되는 경우에 사용하는 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보다 우선합니다. 지속 가능한 필드만 복원합니다. 대기 중인 타이머는 복원되지 않습니다.

일반적으로 유용한 상태는 다음과 같습니다.

  • itemssize: 여전히 대기 중인 항목입니다.
  • isRunning: 자동 처리가 활성화되어 있는지 나타냅니다.
  • isIdle: 실행 중인 큐가 비어 있는지 나타냅니다.
  • isFull: maxSize에 도달했는지 나타냅니다.
  • executionCount: 래핑된 함수가 성공적으로 반환된 항목 수입니다.
  • rejectionCountexpirationCount: 처리되지 않고 제거된 항목 수입니다.
  • status: 'idle', 'running' 또는 'stopped'입니다.

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