본문으로 건너뛰기

Solid 큐 가이드

큐는 작업을 순서가 있는 버퍼에 저장하고 개별적으로 처리합니다. 실행 속도보다 호출이 더 빠르게 들어와도 작업을 버리면 안 되는 경우에 사용하는 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를 반환하거나 동시에 실행되어야 하는 경우 비동기 큐을 사용합니다.

API 선택하기

  • 반응형 대기 항목과 추가 함수에는 createQueuedSignal을 사용합니다.
  • 큐 수명 주기와 순서를 직접 제어하려면 createQueuer를 사용합니다.

큐 내용이 UI를 구동한다면 큐 상태 또는 값 API를 사용합니다. 순서, 용량, 만료, 일시 중지, 재개, 플러시, 수동 처리가 필요하면 인스턴스 API를 사용합니다.

Solid 예시

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

const [items, addItem, queue] = createQueuedSignal(
processJob,
{ wait: 500 },
(state) => ({
items: state.items,
isRunning: state.isRunning,
}),
)

addItem(nextJob())
console.log(items(), queue.state().isRunning)

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

생성 시점에 이미 작업이 있다면 initialItems를 전달합니다. 큐는 일반적인 삽입 및 용량 규칙을 적용하며 started: false를 설정하지 않으면 자동 처리를 즉시 시작할 수 있습니다.

항목 순서 지정하기

자동 처리는 addItemsTo로 새 항목이 들어올 위치를 선택하고 getItemsFrom으로 항목이 나갈 위치를 선택합니다.

FIFO

FIFO는 가장 오래된 항목부터 처리합니다. 기본 동작입니다.

const queuer = createQueuer(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 = createQueuer(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 = createQueuer<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 = createQueuer(processItem, {
wait: 1000,
})

처리 전에 항목을 수집하려면 started: false를 설정합니다.

const queuer = createQueuer(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 = createQueuer(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 = createQueuer(processItem, {
expirationDuration: 5000,
onExpire: (item) => {
console.log('Expired:', item)
},
})

사용자 정의 로직이 필요하면 getIsExpired를 사용합니다.

const queuer = createQueuer(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()은 상태를 기본값인 실행 중인 빈 큐로 복원합니다. 이미 예약된 타임아웃은 지우지 않습니다. 예약된 작업을 취소해야 한다면 stop()을 호출한 다음 reset()을 호출합니다.

queuer.stop()
queuer.reset()

큐 설정 및 관찰하기

향후 동작을 업데이트하려면 setOptions()를 사용합니다. startedsetOptions()로 변경해도 start()stop()을 호출하지 않습니다.

queuer.setOptions({ wait: 250, maxSize: 20 })
queuer.start()

wait 옵션에는 큐어 인스턴스를 받는 함수를 사용할 수 있습니다.

const queuer = createQueuer(processItem, {
wait: (queuer) => (queuer.store.state.size > 20 ? 50 : 250),
})

큐 이벤트에는 다음 콜백을 사용합니다.

  • onItemsChange: 항목이 추가되거나 제거되었습니다.
  • onExecute: 항목이 처리되었습니다.
  • onReject: 항목이 거부되었습니다.
  • onExpire: 항목이 만료되었습니다.

Solid 수명 주기

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

반응형 상태

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

const queuer = createQueuer(processItem, { wait: 250 }, (state) => ({
size: state.size,
isRunning: state.isRunning,
}))

console.log(queuer.state().size, queuer.state().isRunning)

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

initialState로 앱에서 유지한 선택된 큐 상태를 복원할 수 있습니다. items가 포함되어 있으면 initialItems보다 우선하며, 마찬가지로 initialState.isRunningstarted보다 우선합니다. 지속 가능한 필드만 복원합니다. 대기 중인 타이머는 복원되지 않습니다.

일반적으로 유용한 상태에는 다음 항목이 포함됩니다.

  • items, size: 아직 대기 중인 항목입니다.
  • isRunning: 자동 처리가 활성화되었는지 여부입니다.
  • isIdle: 실행 중인 큐가 비어 있는지 여부입니다.
  • isFull: maxSize에 도달했는지 여부입니다.
  • executionCount: 래핑된 함수가 성공적으로 반환한 항목 수입니다.
  • rejectionCount, expirationCount: 처리하지 않고 제거된 항목 수입니다.
  • status: 'idle', 'running', 'stopped' 중 하나입니다.

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