UNPKG

id-scanner-lib

Version:

Browser-based ID card, QR code, and face recognition scanner with liveness detection

283 lines (252 loc) 5.86 kB
/** * @file 重试工具 * @description 提供重试逻辑功能 * @module utils/retry */ /** * 重试选项 */ export interface RetryOptions { /** 最大重试次数 */ maxAttempts?: number; /** 初始等待时间(ms) */ initialDelay?: number; /** 最大等待时间(ms) */ maxDelay?: number; /** 指数退避因子 */ backoffFactor?: number; /** 是否随机抖动 */ jitter?: boolean; /** 重试条件 */ shouldRetry?: (error: unknown) => boolean; } /** * 默认重试选项 */ const DEFAULT_OPTIONS: Required<RetryOptions> = { maxAttempts: 3, initialDelay: 1000, maxDelay: 10000, backoffFactor: 2, jitter: true, shouldRetry: () => true }; /** * 带重试的异步函数包装器 * @param fn 要执行的异步函数 * @param options 重试选项 * @returns 函数结果 */ export async function withRetry<T>( fn: () => Promise<T>, options: RetryOptions = {} ): Promise<T> { const opts = { ...DEFAULT_OPTIONS, ...options }; let lastError: unknown; let delay = opts.initialDelay; for (let attempt = 1; attempt <= opts.maxAttempts; attempt++) { try { return await fn(); } catch (error) { lastError = error; // 检查是否应该重试 if (!opts.shouldRetry(error)) { throw error; } // 如果不是最后一次尝试,等待后重试 if (attempt < opts.maxAttempts) { // 添加随机抖动 const jitterAmount = opts.jitter ? Math.random() * delay : 0; const waitTime = Math.min(delay + jitterAmount, opts.maxDelay); await sleep(waitTime); // 指数退避 delay = Math.min(delay * opts.backoffFactor, opts.maxDelay); } } } throw lastError; } /** * 睡眠函数 * @param ms 毫秒数 */ function sleep(ms: number): Promise<void> { return new Promise(resolve => setTimeout(resolve, ms)); } /** * 创建指数退避重试函数 * @param options 重试选项 * @returns 包装后的重试函数 */ export function createRetryable<T extends (...args: any[]) => Promise<any>>( fn: T, options: RetryOptions = {} ): T { const wrapped = (...args: Parameters<T>): Promise<ReturnType<T>> => { return withRetry(() => fn(...args), options); }; Object.defineProperty(wrapped, 'name', { value: fn.name, configurable: true }); return wrapped as T; } /** * 异步缓存 * @description 用于缓存异步函数的结果 */ export class AsyncCache<T> { private cache: Map<string, { value: T; expiry: number }> = new Map(); private defaultTTL: number; /** * @param defaultTTL 默认过期时间(毫秒) */ constructor(defaultTTL: number = 5 * 60 * 1000) { this.defaultTTL = defaultTTL; } /** * 获取缓存值 * @param key 缓存键 * @returns 缓存值,如果不存在或已过期则返回undefined */ get(key: string): T | undefined { const entry = this.cache.get(key); if (!entry) return undefined; if (Date.now() > entry.expiry) { this.cache.delete(key); return undefined; } return entry.value; } /** * 设置缓存值 * @param key 缓存键 * @param value 缓存值 * @param ttl 过期时间(毫秒) */ set(key: string, value: T, ttl?: number): void { this.cache.set(key, { value, expiry: Date.now() + (ttl || this.defaultTTL) }); } /** * 删除缓存值 * @param key 缓存键 */ delete(key: string): void { this.cache.delete(key); } /** * 清空缓存 */ clear(): void { this.cache.clear(); } /** * 获取缓存大小 */ get size(): number { // 清理过期项 (避免在迭代中删除) const now = Date.now(); const expiredKeys: string[] = []; for (const [key, entry] of this.cache.entries()) { if (now > entry.expiry) { expiredKeys.push(key); } } expiredKeys.forEach(key => this.cache.delete(key)); return this.cache.size; } /** * 异步获取或设置缓存 * @param key 缓存键 * @param fn 获取值的函数 * @param ttl 过期时间 * @returns 缓存值 */ async getOrSet( key: string, fn: () => Promise<T>, ttl?: number ): Promise<T> { const cached = this.get(key); if (cached !== undefined) { return cached; } const value = await fn(); this.set(key, value, ttl); return value; } } /** * 简单信号量 * @description 用于控制并发数量 */ export class Semaphore { private permits: number; private queue: Array<() => void> = []; /** * @param permits 最大许可数 */ constructor(permits: number) { this.permits = permits; } /** * 获取许可 * @returns Promise */ async acquire(): Promise<void> { if (this.permits > 0) { this.permits--; return Promise.resolve(); } return new Promise<void>(resolve => { this.queue.push(resolve); }); } /** * 释放许可 */ release(): void { this.permits++; const next = this.queue.shift(); if (next) { this.permits--; next(); } } /** * 尝试获取许可 * @returns 是否获取成功 */ tryAcquire(): boolean { if (this.permits > 0) { this.permits--; return true; } return false; } /** * 获取当前可用许可数 */ get availablePermits(): number { return this.permits; } } /** * 带信号量的异步函数包装器 * @param fn 异步函数 * @param semaphore 信号量 * @returns 包装后的函数 */ export function withSemaphore<T extends (...args: any[]) => Promise<any>>( fn: T, semaphore: Semaphore ): T { return (async (...args: Parameters<T>): Promise<ReturnType<T>> => { await semaphore.acquire(); try { return await fn(...args); } finally { semaphore.release(); } }) as T; }