UNPKG

needlework

Version:

A fully automated multi-threading utility for JavaScript

11 lines (10 loc) 5.42 kB
/** @preserve @license @cc_on * ---------------------------------------------------------- * needlework version 0.0.7 * A fully automated multi-threading utility for JavaScript * Copyright (c) 2022 Henrik Olofsson * All Rights Reserved. MIT License * https://mit-license.org/ * ---------------------------------------------------------- */ !function(){"use strict";!function(){class e{constructor(){this.threadQueue=[],this.numberOfRunningThreads=0,this.maxThreads=-1,this.metaData=new WeakMap,this.MAXIMUM_IDLE_TIME_MS=1e4}getMaxThreadCount(){return this.maxThreads}setMaxThreadCount(e){this.maxThreads=e}setThreadMaxIdleTime(e){this.MAXIMUM_IDLE_TIME_MS=e}hasNext(){return!!this.threadQueue.length}createInlineWorker(e,t){return this.createInlineWorker(e,t)}terminate(e){if(this.metaData.has(e)){const t=this.metaData.get(e);t.terminationTimeout&&clearTimeout(t.terminationTimeout),t.freeThreads.forEach((e=>{e.terminate()})),this.metaData.delete(e)}this.threadQueue.filter((t=>t[1]===e)).forEach((e=>e[0].reject("Thread was terminated."))),this.threadQueue=this.threadQueue.filter((t=>t[1]!==e))}getDependencyString(e,t=""){if(e._needlework&&e._needlework.dependencyString&&(t+=`${e._needlework.dependencyString}\n\t\t\t\n\t\t\t`),e._needlework&&e._needlework.dependencies){const n=e._needlework.dependencies;Object.keys(n).forEach((e=>{const r=n[e];t+=`var ${e} = ${r.toString?r.toString():r}\n\t\t\t\t\n\t\t\t\t`,t=this.getDependencyString(r,t)}))}return t}async run(e,...t){this.metaData.has(e)||this.metaData.set(e,{pendingTasks:0,terminationTimeout:void 0,dependencyString:this.getDependencyString(e),freeThreads:[]});const n=this.metaData.get(e);let r,a;n.terminationTimeout&&(clearTimeout(n.terminationTimeout),delete n.terminationTimeout),n.pendingTasks++;const s=new Promise(((e,t)=>{r=e,a=t}));return this.threadQueue.push([{resolve:r,reject:a},e,[...t]]),this.numberOfRunningThreads!==this.maxThreads&&this.executeQueueLoop(),s}async executeQueueLoop(){for(;this.hasNext();){const e=this.threadQueue.splice(0,1)[0],t=e[0].resolve,n=e[0].reject,r=e[1],a=e[2],s=this.metaData.get(r).freeThreads;s.length||s.push(this.createInlineWorker(r,this.metaData.get(r).dependencyString));const i=s.splice(0,1)[0];this.numberOfRunningThreads++;try{await this.executeWorker(i,...a).then((e=>{this.handleThreadCompleted(r,i),t(e)})).catch((e=>{this.handleThreadCompleted(r,i),n(e)}))}catch(e){console.error(e)}this.numberOfRunningThreads--}}executeWorker(e,...t){return new Promise(((n,r)=>{e.onmessage=e=>{n(e.data)},e.onerror=r,e.postMessage(t)}))}handleThreadCompleted(e,t){const n=this.metaData.get(e);n.freeThreads.push(t),n.pendingTasks--,n.pendingTasks||this.MAXIMUM_IDLE_TIME_MS&&(n.terminationTimeout=setTimeout((()=>{this.terminate(e)}),this.MAXIMUM_IDLE_TIME_MS))}}class t extends e{constructor(){super(),this.maxThreads=Math.max(navigator.hardwareConcurrency-1,1)}createInlineWorker(e,t){const n=e.toString(),r=`\n ${t}\n \n function execute(${n.substring(n.indexOf("(")+1,n.indexOf(")"))}) {\n ${n.substring(n.indexOf("{")+1,n.lastIndexOf("}"))}\n }\n self.onmessage = async (params) => {\n let result = []\n for(let i = 0; i < params.data.length; i++) {\n result.push(execute(...params.data[i]))\n }\n for(let i = 0; i < params.data.length; i++)\n if(result[i] instanceof Promise) {\n result[i] = await result[i]\n }\n postMessage(result)\n }\n `;return new Worker(URL.createObjectURL(new Blob([r],{type:"text/javascript"})))}}class n extends e{constructor(){super(),this.maxThreads=Math.max(require("os").cpus().length-1,1),this.Worker=require("worker_threads").Worker}createInlineWorker(e,t){const n=e.toString(),r=`\n const { workerData, parentPort } = require("worker_threads")\n \n ${t}\n \n function execute(${n.substring(n.indexOf("(")+1,n.indexOf(")"))}) {\n ${n.substring(n.indexOf("{")+1,n.lastIndexOf("}"))}\n }\n parentPort.on("message", async (params) => {\n let result = []\n for(let i = 0; i < params.length; i++) {\n result.push(execute(...params[i]))\n }\n for(let i = 0; i < params.length; i++)\n if(result[i] instanceof Promise) {\n result[i] = await result[i]\n }\n parentPort.postMessage(result)\n })\n `,a=new this.Worker(r,{eval:!0}),s={onmessage:()=>{},onerror:()=>{},postMessage:(...e)=>a.postMessage(...e),terminate:()=>a.terminate()};return a.on("message",(e=>s.onmessage({data:e}))),a.on("error",((...e)=>s.onerror(...e))),s}}let r;r="undefined"==typeof window?new n:new t,Function.prototype.runThread=function(...e){return r.run(this,[...e]).then((e=>e[0]))},Function.prototype.runManyInThread=function(...e){return r.run(this,...e)},Function.prototype.terminateThreads=function(){r.terminate(this)},Function.prototype.getMaxThreadCount=function(){return r.getMaxThreadCount()},Function.prototype.setMaxThreadCount=function(e){return r.setMaxThreadCount(e)},Function.prototype.setThreadMaxIdleTime=function(e){return r.setThreadMaxIdleTime(e)}}()}();