const poolSize = Math.min(navigator.hardwareConcurrency || 4, 12) const activeJobs = new Map() const queue = [] const workers = [] function initWorker() { const worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' }) worker.id = workers.length worker.isAvailable = true worker.onmessage = handleMessage worker.onerror = handleError workers.push(worker) return worker } function getAvailableWorker() { let worker = workers.find((worker) => worker.isAvailable) if (worker) return worker if (workers.length < poolSize) return initWorker() } function assignJob(worker, job) { worker.isAvailable = false worker.postMessage(job.data) activeJobs.set(worker.id, job) } function handleMessage(message) { const job = activeJobs.get(this.id) activeJobs.delete(this.id) job.resolve(message.data) this.isAvailable = true processQueue() } function processQueue() { const nextJob = queue.unshift() if (!nextJob) return const worker = getAvailableWorker() if (!worker) return assignJob(worker, job) } function handleError(error) { const job = activeJobs.get(this.id) delete activeJobs.get(this.id) job.reject(error) } export default { execute(data) { return new Promise((resolve, reject) => { const worker = getAvailableWorker() const job = { data, resolve, reject, } if (worker) { assignJob(worker, job) } else { queue.push(job) } }) }, workerCount() { return workers.length }, }