| 8 | * 控制并发操作数量,防止过多并发请求 |
| 9 | */ |
| 10 | export class RateLimiter { |
| 11 | private queue: Array<() => void> = []; |
| 12 | |
| 13 | private running = 0; |
| 14 | |
| 15 | private maxConcurrent: number; |
| 16 | |
| 17 | constructor(maxConcurrent = 5) { |
| 18 | this.maxConcurrent = maxConcurrent; |
| 19 | } |
| 20 | |
| 21 | /** |
| 22 | * 执行限速操作 |
| 23 | * @param fn 要执行的操作函数 |
| 24 | * @param op 操作类型,用于在遇到 429 时判断是否允许自动重试。默认值 "unknown" 不在白名单内,会被视为不可重试 |
| 25 | * @returns 操作结果 |
| 26 | */ |
| 27 | async execute<T>(fn: () => Promise<T>, op = "unknown"): Promise<T> { |
| 28 | // 如果当前运行的操作数已达到上限,则等待 |
| 29 | while (this.running >= this.maxConcurrent) { |
| 30 | await new Promise<void>((resolve) => { |
| 31 | this.queue.push(resolve); |
| 32 | }); |
| 33 | } |
| 34 | |
| 35 | this.running++; |
| 36 | try { |
| 37 | return await this.executeWithRetry(fn, op); |
| 38 | } finally { |
| 39 | this.running--; |
| 40 | // 执行完成后,从队列中取出下一个等待的操作 |
| 41 | const next = this.queue.shift(); |
| 42 | if (next) { |
| 43 | next(); |
| 44 | } |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | /** |
| 49 | * 执行操作并处理 429 错误重试 |
| 50 | * @param fn 要执行的操作函数 |
| 51 | * @param op 操作类型,用于判定该操作在遇到 429 时是否进入指数退避重试 |
| 52 | * @returns 操作结果 |
| 53 | */ |
| 54 | private async executeWithRetry<T>(fn: () => Promise<T>, op: string): Promise<T> { |
| 55 | // 最多重试 10 次 |
| 56 | for (let i = 0; i <= 10; i++) { |
| 57 | try { |
| 58 | return await fn(); |
| 59 | } catch (error) { |
| 60 | // 检查错误字符串中是否包含 429 |
| 61 | const errorStr = String(error).toLowerCase(); |
| 62 | if (this.shouldRetry429(op, ` ${errorStr} `) && i < 10) { |
| 63 | // 遇到 429 错误且未达到重试上限,采用指数退避策略延迟后继续重试 |
| 64 | const delay = Math.min(2000 * Math.pow(2, i), 60000); |
| 65 | await new Promise((resolve) => setTimeout(resolve, delay)); |
| 66 | // 继续下一次循环重试 |
| 67 | continue; |
nothing calls this directly
no outgoing calls
no test coverage detected