바닐라 비동기 요청률 제한 가이드
비동기 요청률 제한은 요청률 제한 가이드에서 설명한 창 동작을 유지하면서 Promise 결과, 재시도, 오류 콜백, 진행 중인 작업 제어 기능을 추가합니다.
수락된 작업이 필요한 값을 반환하거나 거부될 수 있거나 재시도 및 중단 지원이 필요할 때 사용합니다. 즉각적인 수락 또는 거부 불리언 값만 필요하다면 동기 제한기를 사용합니다.
빠른 시작
호출 가능한 함수만 필요하다면 asyncRateLimit을 사용합니다.
import { asyncRateLimit } from '@tanstack/pacer'
const loadUser = asyncRateLimit(
async (id: string) => {
const response = await fetch(`/api/users/${id}`)
if (!response.ok) throw new Error('Request failed')
return response.json()
},
{ limit: 5, window: 60_000 },
)
const user = await loadUser('123')
메서드, 상태 또는 콜백이 필요하다면 AsyncRateLimiter를 사용합니다.
import { AsyncRateLimiter } from '@tanstack/pacer'
const limiter = new AsyncRateLimiter(loadUserFromApi, {
limit: 5,
window: 60_000,
onReject: (args, limiter) => {
console.log(
`Rejected ${args[0]}; retry in ${limiter.getMsUntilNextWindow()}ms`,
)
},
})
const user = await limiter.maybeExecute('123')
수락 및 거부된 호출
maybeExecute()는 두 가지 정상 결과를 갖는 Promise를 반환합니다.
- 수락된 호출은 함수를 실행하고 그 결과로 이행됩니다.
- 창에서 거부된 호출은 함수를 실행하지 않고
undefined로 이행됩니다.
undefined도 유효한 함수 결과라면 onReject를 사용하거나 호출 전에 용량을 비교합니다.
if (limiter.getRemainingInWindow() > 0) {
const result = await limiter.maybeExecute('123')
}
동기 제한기와 달리 수락된 호출은 겹칠 수 있습니다. limit은 한 번에 활성화할 수 있는 실행 수가 아니라 창 안에서 시작할 수 있는 실행 수를 제어합니다.
limit: 3
start A ───────────────── finish A
start B ───── finish B
start C ─────────────────── finish C
call D rejected
동시 실행 제한이 필요하거나 초과 작업을 거부하는 대신 대기시켜야 한다면 비동기 큐를 사용합니다.
창 동작
창 타입은 동기 제한기와 동일합니다.
fixed는 첫 번째로 수락된 실행과 함께 창을 시작합니다. 해당 창의 모든 타임스탬프가 함께 만료됩니다.sliding은 각 수락된 타임스탬프마다 자체window기간이 지날 때까지 유지합니다.
기본값은 fixed입니다. getRemainingInWindow()는 가능한 시작 횟수를 보고하고 getMsUntilNextWindow()는 거부된 호출자가 용량이 생길 때까지 기다려야 하는 시간을 보고합니다.
수락된 실행은 시작될 때 용량을 소비합니다. 비동기 함수가 나중에 실패하거나 중단되어도 횟수에 포함됩니다. 해당 수락된 실행의 재시도는 요청률 제한 타임스탬프를 추가하지 않습니다.
오류 및 콜백
비동기 요청률 제한기는 각 결과에 대해 다음 콜백을 제공합니다.
onSuccess(result, args, limiter)는 수락된 실행이 성공한 후 실행됩니다.onError(error, args, limiter)는 재시도가 실패한 후 실행됩니다.onSettled(args, limiter)는 어느 실행 결과든 완료된 후 실행됩니다.onReject(args, limiter)는 창에 용량이 없을 때 실행됩니다.
거부는 실행 오류가 아닙니다. onError나 onSettled를 호출하지 않습니다.
onError가 없으면 throwOnError의 기본값이 true이므로 수락된 실행이 실패할 때 Promise가 거부됩니다. onError를 제공하면 이 기본값이 false로 바뀌고 Promise는 현재 lastResult로 이행됩니다. 다른 동작이 필요하다면 throwOnError를 명시적으로 설정합니다.
수락된 실행 재시도
각 수락된 실행의 재시도를 asyncRetryerOptions로 설정합니다.
const limiter = new AsyncRateLimiter(sendRequest, {
limit: 5,
window: 60_000,
asyncRetryerOptions: {
maxAttempts: 3,
backoff: 'exponential',
baseWait: 1000,
jitter: 0.2,
},
})
maxAttempts에는 첫 시도가 포함됩니다. 따라서 수락된 요청률 제한 슬롯 하나가 다운스트림 서비스에 여러 번의 시도를 발생시킬 수 있습니다. 요청률 제한과 재시도를 결합하기 전에 해당 서비스 자체의 제한을 고려합니다. 재시도 안전성은 비동기 재시도 가이드를 참고합니다.
활성 작업 중단
abort()는 모든 활성 실행을 중단합니다. 실행의 타임스탬프를 제거하거나 창 용량을 복구하지는 않습니다. 취소가 전파되도록 각 실행의 신호를 기반 작업에 전달합니다.
const limiter = new AsyncRateLimiter(
async (id: string) => {
return fetch(`/api/users/${id}`, {
signal: limiter.getAbortSignal() ?? undefined,
})
},
{ limit: 5, window: 60_000 },
)
limiter.abort()
여러 실행이 겹칠 때 인수 없는 getAbortSignal()은 가장 최근에 시작된 실행을 가리킵니다. 특정 활성 실행을 대상으로 하려면 해당 실행의 maybeExecuteCount를 전달합니다.
재설정
reset()은 요청률 제한 타임스탬프를 지우고 기본 상태를 복원합니다. 활성 기반 작업의 중지를 보장하지 않으므로 완전히 정리해야 한다면 먼저 중단합니다.
limiter.abort()
limiter.reset()
재설정하면 용량이 즉시 복구됩니다. 설정된 제한을 우회하는 수단으로 사용하지 말고 실제로 새로운 제한 기간을 시작할 때만 수행합니다.
설정
enabled, limit, window에는 값 또는 제한기 인스턴스를 받는 함수를 지정할 수 있습니다. setOptions()는 새 옵션을 현재 설정에 병합합니다.
limiter.setOptions({
enabled: (limiter) => limiter.store.state.errorCount < 3,
limit: (limiter) =>
limiter.store.state.rejectionCount > 10 ? 2 : 5,
})
limit, window, windowType을 변경해도 기존 실행 기록은 지워지지 않습니다. 새 설정을 새로운 창으로 시작해야 한다면 reset()을 호출합니다. 비활성화된 제한기는 함수를 실행하거나 용량을 소비하지 않으며 호출은 undefined로 이행됩니다.
재사용할 수 있고 타입 검사를 거친 옵션 객체를 정의하려면 asyncRateLimiterOptions()를 사용합니다.
상태
클래스는 limiter.store에 상태를 저장합니다.
애플리케이션이 유지한 일부 상태를 복원하려면 부분 스냅샷을 initialState로 전달합니다. 이 스냅샷은 기본값과 병합됩니다. 지속 가능한 필드만 복원합니다. 대기 중인 타이머와 활성 실행은 복원되지 않습니다.
일반적으로 유용한 속성은 다음과 같습니다.
executionTimes: 창에서 여전히 사용하는 수락된 시작 시각입니다.isExceeded: 현재 제한에 도달했는지 나타냅니다.isExecuting: 수락된 실행이 하나 이상 활성 상태인지 나타냅니다.rejectionCount: 창에서 거부된 호출 수입니다.lastResult: 가장 최근의 성공 결과입니다.successCount,errorCount,settleCount: 실행 결과 횟수입니다.
전체 옵션과 상태 타입은 AsyncRateLimiter API 레퍼런스를 참고합니다.