'use strict'; const workerUtil = { createWorker({url, lifeTime = 300}) { let worker; let id; let timer; const pendingResponse = new Map(); return new Proxy({}, { get: (target, prop) => (...args) => { if (!worker) { init(); } return invoke(prop, args); }, }); function init() { id = 0; worker = new Worker(url); worker.onmessage = onMessage; } function uninit() { worker.onmessage = null; worker.terminate(); worker = null; } function onMessage({data: {id, data, error}}) { pendingResponse.get(id)[error ? 'reject' : 'resolve'](data); pendingResponse.delete(id); if (!pendingResponse.size && lifeTime >= 0) { timer = setTimeout(uninit, lifeTime * 1000); } } function invoke(action, args) { return new Promise((resolve, reject) => { pendingResponse.set(id, {resolve, reject}); clearTimeout(timer); worker.postMessage({id, action, args}); id++; }); } }, createAPI(methods) { self.onmessage = async ({data: {id, action, args}}) => { let data, error; try { data = await methods[action](...args); } catch (err) { error = true; data = workerUtil.cloneError(err); } self.postMessage({id, data, error}); }; }, cloneError(err) { return Object.assign({ name: err.name, stack: err.stack, message: err.message, lineNumber: err.lineNumber, columnNumber: err.columnNumber, fileName: err.fileName }, err); }, loadScript(...urls) { urls = urls.filter(u => !workerUtil._loadedScripts.has(u)); if (!urls.length) { return; } self.importScripts(...urls); urls.forEach(u => workerUtil._loadedScripts.add(u)); }, _loadedScripts: new Set(), };