id-scanner-lib
Version:
Browser-based ID card, QR code, and face recognition scanner with liveness detection
213 lines (186 loc) • 5.24 kB
text/typescript
/**
* @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 = []
}
}