UNPKG

id-scanner-lib

Version:

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

213 lines (186 loc) 5.24 kB
/** * @file Worker工具 * @description 提供Web Worker相关的工具函数 * @module utils/worker */ /** * 检查是否支持Web Worker * @returns 是否支持Web Worker */ export function isWorkerSupported(): boolean { return typeof Worker !== 'undefined'; } /** * 创建Worker * @param workerFunction Worker函数 * @returns Worker实例 */ export function createWorker<TInput, TOutput>( workerFunction: (input: TInput) => TOutput | Promise<TOutput> ): { postMessage: (input: TInput) => Promise<TOutput>; terminate: () => void; } { // 检查是否支持Web Worker if (!isWorkerSupported()) { // 回退到主线程执行 return { postMessage: async (input: TInput) => { return await Promise.resolve(workerFunction(input)); }, terminate: () => {} }; } // 将函数转换为字符串 const workerFunctionStr = workerFunction.toString(); // 创建Worker脚本 const workerScript = ` // 定义Worker函数 const workerFunction = ${workerFunctionStr}; // 监听消息 self.addEventListener('message', async (event) => { try { const input = event.data; const result = await workerFunction(input); self.postMessage({ success: true, result }); } catch (error) { self.postMessage({ success: false, error: error instanceof Error ? error.message : String(error) }); } }); `; // 创建Blob URL const blob = new Blob([workerScript], { type: 'application/javascript' }); const url = URL.createObjectURL(blob); // 创建Worker const worker = new Worker(url); // 创建Promise映射 const promiseMap = new Map<number, { resolve: (value: TOutput) => void; reject: (reason: any) => void; }>(); // 消息计数器 let messageCounter = 0; // 监听Worker消息 worker.addEventListener('message', (event) => { const { messageId, success, result, error: _error } = event.data; // 查找对应的Promise const promiseHandlers = promiseMap.get(messageId); if (promiseHandlers) { if (success) { promiseHandlers.resolve(result); } else { promiseHandlers.reject(new Error(_error)); } // 删除Promise映射 promiseMap.delete(messageId); } }); // 返回Worker接口 return { postMessage: (input: TInput): Promise<TOutput> => { return new Promise<TOutput>((resolve, reject) => { // 生成消息ID const messageId = messageCounter++; // 保存Promise处理函数 promiseMap.set(messageId, { resolve, reject }); // 发送消息到Worker worker.postMessage({ messageId, input }); }); }, terminate: () => { // 终止Worker worker.terminate(); // 释放Blob URL URL.revokeObjectURL(url); // 拒绝所有未完成的Promise for (const [, { reject: _reject }] of promiseMap) { _reject(new Error('Worker已终止')); } // 清空Promise映射 promiseMap.clear(); } }; } /** * 工作线程池 * 用于管理和重用Worker线程,避免频繁创建和销毁 */ export class WorkerPool<T, R> { private workers: Array<{ worker: ReturnType<typeof createWorker<T, R>> busy: boolean }> = [] /** * 创建工作线程池 * * @param workerFunction 要在Worker中执行的函数 * @param size 池中Worker的数量 */ constructor( private workerFunction: (data: T) => Promise<R> | R, private size: number = navigator.hardwareConcurrency || 4 ) { // 更精确的 workerFunction 类型 // 预创建Workers for (let i = 0; i < size; i++) { this.workers.push({ worker: createWorker<T, R>(workerFunction), busy: false, }) } } /** * 获取一个可用的Worker * * @returns Worker包装对象 */ private getAvailableWorker(): ReturnType<typeof createWorker<T, R>> { // 找到第一个空闲的Worker const availableWorker = this.workers.find((w) => !w.busy) if (availableWorker) { availableWorker.busy = true return availableWorker.worker } // 如果没有空闲Worker,创建一个新的 const worker = createWorker<T, R>(this.workerFunction) this.workers.push({ worker, busy: true }) return worker } /** * 执行任务 * * @param data 要处理的数据 * @returns 处理结果的Promise */ async execute(data: T): Promise<R> { const worker = this.getAvailableWorker() try { const result = await worker.postMessage(data) // 标记Worker为空闲 const workerEntry = this.workers.find((w) => w.worker === worker) if (workerEntry) { workerEntry.busy = false } return result } catch (error) { // 出错时也标记为空闲 const workerEntry = this.workers.find((w) => w.worker === worker) if (workerEntry) { workerEntry.busy = false } throw error } } /** * 终止所有Worker */ terminate(): void { this.workers.forEach(({ worker }) => { worker.terminate() }) this.workers = [] } }