├── gitignore.txt ├── dist ├── WorkerBee.test.d.ts ├── workerCode.d.ts ├── index.d.ts ├── index.js.map ├── index.js ├── WorkerBee.js.map ├── WorkerBee.d.ts ├── quickWorkers.d.ts ├── WorkerBee.js ├── quickWorkers.js.map ├── workerCode.js.map ├── workerCode.js ├── quickWorkers.js ├── WorkerBee.test.js.map └── WorkerBee.test.js ├── .gitignore ├── src ├── index.ts ├── WorkerBee.ts ├── workerCode.ts ├── quickWorkers.ts └── WorkerBee.test.ts ├── karma.conf.js ├── tsconfig.json ├── package.json ├── LICENSE.txt └── README.md /gitignore.txt: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage -------------------------------------------------------------------------------- /dist/WorkerBee.test.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | package-lock.json -------------------------------------------------------------------------------- /dist/workerCode.d.ts: -------------------------------------------------------------------------------- 1 | export declare function workerCode(self: Worker): void; 2 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { createWorker, miniWorker } from "./quickWorkers"; 2 | import { WorkerBee } from "./WorkerBee"; 3 | 4 | export { WorkerBee, createWorker, miniWorker }; -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | import { createWorker, miniWorker } from "./quickWorkers"; 2 | import { WorkerBee } from "./WorkerBee"; 3 | export { WorkerBee, createWorker, miniWorker }; 4 | -------------------------------------------------------------------------------- /dist/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,iDAA0D;AAGtC,6FAHX,2BAAY,OAGW;AAAE,2FAHX,yBAAU,OAGW;AAF5C,2CAAwC;AAE/B,0FAFA,qBAAS,OAEA"} -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | 4 | frameworks: ["jasmine", "karma-typescript"], 5 | files: [ 6 | { pattern: "src/**/*.ts" } 7 | ], 8 | port: 5000, 9 | 10 | preprocessors: { 11 | "**/*.ts": ["karma-typescript"] 12 | }, 13 | 14 | reporters: ["dots", "karma-typescript"], 15 | }); 16 | }; -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.miniWorker = exports.createWorker = exports.WorkerBee = void 0; 4 | const quickWorkers_1 = require("./quickWorkers"); 5 | Object.defineProperty(exports, "createWorker", { enumerable: true, get: function () { return quickWorkers_1.createWorker; } }); 6 | Object.defineProperty(exports, "miniWorker", { enumerable: true, get: function () { return quickWorkers_1.miniWorker; } }); 7 | const WorkerBee_1 = require("./WorkerBee"); 8 | Object.defineProperty(exports, "WorkerBee", { enumerable: true, get: function () { return WorkerBee_1.WorkerBee; } }); 9 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "outDir": "./dist/", 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "noImplicitReturns": true, 9 | "noFallthroughCasesInSwitch": true, 10 | "sourceMap": true, 11 | "declaration": true, 12 | "downlevelIteration": true, 13 | "experimentalDecorators": true, 14 | "moduleResolution": "node", 15 | "importHelpers": true, 16 | "target": "es2017", 17 | "module": "commonjs", 18 | "lib": [ 19 | "es2016", 20 | "dom" 21 | ] 22 | } 23 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@lukeschaefer/worker-bee", 3 | "version": "1.0.5", 4 | "description": "Tiny utility for pushing tasks into an async Web Worker", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "build": "npx tsc", 9 | "test": "karma start" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/lukeschaefer/WorkerBee.git" 14 | }, 15 | "keywords": [ 16 | "Web Worker", 17 | "async", 18 | "threading" 19 | ], 20 | "author": "Luke Schaefer", 21 | "license": "ISC", 22 | "bugs": { 23 | "url": "https://github.com/lukeschaefer/WorkerBee/issues" 24 | }, 25 | "homepage": "https://github.com/lukeschaefer/WorkerBee#readme", 26 | "devDependencies": { 27 | "@types/jasmine": "^3.4.0", 28 | "jasmine-core": "^3.3.0", 29 | "karma": "^6.3.16", 30 | "karma-chrome-launcher": "^3.1.0", 31 | "karma-cli": "^2.0.0", 32 | "karma-jasmine": "^2.0.1", 33 | "karma-typescript": "^5.5.3", 34 | "typescript": "^4.5.5" 35 | }, 36 | "dependencies": {} 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Luke Schaefer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /dist/WorkerBee.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"WorkerBee.js","sourceRoot":"","sources":["../src/WorkerBee.ts"],"names":[],"mappings":";;;AACA,6CAA0C;AAG1C,MAAa,SAAS;IAIpB,YAAY,OAAkC,EAAE;QAHhD,YAAO,GAAG,CAAC,CAAC;QAIV,IAAI,YAAY,GAAG,IAAI,oBAAY,CAAC,UAAU,SAAS,CAAC;QACxD,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,IAAI,EAAE,wBAAwB,EAAE,CAAC,CAAC;QAC1E,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,WAAW,CAAC,OAAuB;QACjC,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;QACjD,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;QAEjD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,QAAQ,GAAG,CAAC,CAAoB,EAAE,EAAE;gBACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAoB,CAAC;gBACvD,IAAI,QAAQ,CAAC,EAAE,KAAK,OAAO,CAAC,EAAE,EAAE;oBAE9B,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;oBAErD,IAAI,QAAQ,CAAC,IAAI,IAAI,SAAS,EAAE;wBAC9B,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;qBACvB;yBAAM;wBACL,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;qBACxB;iBACF;YACH,CAAC,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AA/BD,8BA+BC;AAED,wCAAwC;AAC3B,QAAA,YAAY,GAAG;IAC1B,UAAU,EAAE,uBAAU,CAAC,QAAQ,EAAE;CAClC,CAAA"} -------------------------------------------------------------------------------- /dist/WorkerBee.d.ts: -------------------------------------------------------------------------------- 1 | export declare class WorkerBee { 2 | counter: number; 3 | worker: Worker; 4 | constructor(init?: { 5 | [key: string]: Function; 6 | }); 7 | sendMessage(message: WorkerBMessage): Promise; 8 | } 9 | /** Used so we can override in tests. */ 10 | export declare const WorkerConfig: { 11 | workerCode: string; 12 | }; 13 | export declare type WorkerBResponse = { 14 | type: 'success' | 'failure'; 15 | body: any; 16 | id: string; 17 | }; 18 | export declare type WorkerBMessage = { 19 | id?: string; 20 | } & (SetProperty | CallFunctionMessage | ImportScriptsMessage | GetProperty | SetFunctionMessage); 21 | export declare type SetProperty = { 22 | type: 'setProperty'; 23 | name: string; 24 | value: any; 25 | }; 26 | export declare type GetProperty = { 27 | type: 'getProperty'; 28 | name: string; 29 | }; 30 | export declare type ImportScriptsMessage = { 31 | type: 'importScripts'; 32 | scripts: string[]; 33 | }; 34 | export declare type CallFunctionMessage = { 35 | type: 'callFunction'; 36 | name: string; 37 | args: any[]; 38 | }; 39 | export declare type SetFunctionMessage = { 40 | type: 'setFunction'; 41 | name: string; 42 | body: string; 43 | }; 44 | -------------------------------------------------------------------------------- /dist/quickWorkers.d.ts: -------------------------------------------------------------------------------- 1 | /** Utils for easy use of WorkerBee workers */ 2 | export declare function miniWorker

(func: (P)): (...args: Parameters

) => Promise>; 3 | export declare function createWorker(init: T): WorkerInterface; 4 | export declare type WorkerUtils = { 5 | destroy: () => void; 6 | importScripts: (scripts: string[]) => Promise; 7 | }; 8 | export declare type AnyFunction = (...args: any[]) => any; 9 | export declare type WorkerFunction = (...args: Parameters) => Promise>; 10 | export declare type ToWorkerProperty = T extends AnyFunction ? WorkerFunction : Promise; 11 | export declare type WorkerMap = { 12 | [key: string]: (any); 13 | }; 14 | export declare type WorkerProperties = { 15 | [key in keyof T]: ToWorkerProperty; 16 | }; 17 | export declare type SetterKey = `set${Capitalize}`; 18 | export declare type SetterFunc = (value: T) => Promise; 19 | export declare type WorkerSetters = { 20 | [Key in keyof T & string as SetterKey]: SetterFunc; 21 | }; 22 | export declare type WorkerInterface = WorkerProperties & WorkerSetters & WorkerUtils; 23 | -------------------------------------------------------------------------------- /dist/WorkerBee.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.WorkerConfig = exports.WorkerBee = void 0; 4 | const workerCode_1 = require("./workerCode"); 5 | class WorkerBee { 6 | constructor(init = {}) { 7 | this.counter = 0; 8 | let workerString = `(${exports.WorkerConfig.workerCode})(self)`; 9 | const blob = new Blob([workerString], { type: 'application/javascript' }); 10 | this.worker = new Worker(URL.createObjectURL(blob)); 11 | } 12 | sendMessage(message) { 13 | message.id = `${message.type}_${this.counter++}`; 14 | this.worker.postMessage(JSON.stringify(message)); 15 | return new Promise((resolve, reject) => { 16 | const listener = (e) => { 17 | const response = JSON.parse(e.data); 18 | if (response.id === message.id) { 19 | this.worker.removeEventListener('message', listener); 20 | if (response.type == "failure") { 21 | reject(response.body); 22 | } 23 | else { 24 | resolve(response.body); 25 | } 26 | } 27 | }; 28 | this.worker.addEventListener('message', listener); 29 | }); 30 | } 31 | } 32 | exports.WorkerBee = WorkerBee; 33 | /** Used so we can override in tests. */ 34 | exports.WorkerConfig = { 35 | workerCode: workerCode_1.workerCode.toString() 36 | }; 37 | //# sourceMappingURL=WorkerBee.js.map -------------------------------------------------------------------------------- /dist/quickWorkers.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"quickWorkers.js","sourceRoot":"","sources":["../src/quickWorkers.ts"],"names":[],"mappings":";AAAA,8CAA8C;;;AAE9C,2CAAwC;AAExC,SAAgB,UAAU,CAAwB,IAAS;IACzD,MAAM,MAAM,GAAG,YAAY,CAAC;QAC1B,IAAI,EAAE,IAAI;KACX,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AALD,gCAKC;AAED,SAAgB,YAAY,CAAsB,IAAO;IACvD,MAAM,MAAM,GAAG,IAAI,qBAAS,EAAE,CAAC;IAE/B,MAAM,aAAa,GAAG,EAAyB,CAAC;IAChD,MAAM,OAAO,GAAG,EAAsB,CAAC;IACvC,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE;QACvB,qCAAqC;QACrC,IAAK,IAAI,CAAC,IAAI,CAAS,YAAY,QAAQ,EAAE;YAC3C,IAAI,aAAa,GAAG,MAAM,CAAC,WAAW,CAAC;gBACrC,IAAI,EAAE,aAAa;gBACnB,IAAI;gBACJ,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;aAC5B,CAAC,CAAC;YAEH,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,IAAW,EAAE,EAAE;gBAC9C,MAAM,aAAa,CAAC;gBACpB,OAAO,MAAM,MAAM,CAAC,WAAW,CAAC;oBAC9B,IAAI,EAAE,cAAc;oBACpB,IAAI,EAAE,IAAI;oBACV,IAAI;iBACL,CAAC,CAAC;YACL,CAAC,CAA+C,CAAC;SAClD;aAAM;YACL,oDAAoD;YACpD,IAAI,aAAa,GAAG,MAAM,CAAC,WAAW,CAAC;gBACrC,IAAI,EAAE,aAAa;gBACnB,IAAI;gBACJ,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC;aAClB,CAAC,CAAC;YAEH,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAA8G,CAAC;YAC7K,MAAM,cAAc,GAAG,KAAK,EAAE,KAAU,EAAE,EAAE;gBAC1C,MAAM,aAAa,CAAC;gBACpB,OAAO,MAAM,MAAM,CAAC,WAAW,CAAC;oBAC9B,IAAI,EAAE,aAAa;oBACnB,IAAI;oBACJ,KAAK;iBACN,CAAC,CAAC;YACL,CAAC,CAAC;YACF,OAAO,CAAC,UAAU,CAAC,GAAG,cAAc,CAAC;YAErC,MAAM,CAAC,cAAc,CAAC,aAAa,EAAE,IAAI,EAAE;gBACzC,UAAU,EAAE,IAAI;gBAChB,GAAG,EAAE,KAAK;oBACR,MAAM,aAAa,CAAC;oBACpB,OAAO,MAAM,MAAM,CAAC,WAAW,CAAC;wBAC9B,IAAI,EAAE,aAAa;wBACnB,IAAI,EAAE,IAAI;qBACX,CAAC,CAAC;gBACL,CAAC;aACF,CAAC,CAAC;SACJ;KACF;IAED,MAAM,KAAK,GAAgB;QACzB,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE;QACxC,aAAa,EAAE,KAAK,EAAE,OAAiB,EAAE,EAAE;YACzC,OAAO,MAAM,MAAM,CAAC,WAAW,CAAC;gBAC9B,IAAI,EAAE,eAAe;gBACrB,OAAO;aACR,CAAC,CAAC;QACL,CAAC;KACF,CAAC;IAEF,iEAAiE;IACjE,OAAO,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;AACtD,CAAC;AAlED,oCAkEC"} -------------------------------------------------------------------------------- /dist/workerCode.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"workerCode.js","sourceRoot":"","sources":["../src/workerCode.ts"],"names":[],"mappings":";;;AAGA,SAAgB,UAAU,CAAC,IAAY;IACrC,MAAM,OAAO,GAAc,EAAE,CAAC;IAE9B,SAAS,WAAW,CAAC,OAAwB;QAC3C,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5C,CAAC;IAED,SAAS,WAAW,CAAC,IAAY,EAAE,KAAa,EAAE,EAAU;QAC1D,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;QACtB,WAAW,CAAC;YACV,EAAE;YACF,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,KAAK;SACZ,CAAC,CAAC;IACL,CAAC;IAED,SAAS,WAAW,CAAC,IAAY,EAAE,IAAY,EAAE,EAAU;QACzD,IAAI,IAAI,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC;QACrB,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;QAEvB,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QACrB,WAAW,CAAC;YACV,EAAE;YACF,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,IAAI;SACX,CAAC,CAAC;IACL,CAAC;IAED,SAAS,YAAY,CAAC,OAA8C;QAClE,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,QAAQ,EAAE;YACtE,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;YAClE,WAAW,CAAC;gBACV,EAAE,EAAE,OAAO,CAAC,EAAG;gBACf,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,MAAM;aACb,CAAC,CAAC;SACJ;IACH,CAAC;IAED,SAAS,WAAW,CAAC,OAAsC;QACzD,WAAW,CAAC;YACV,EAAE,EAAE,OAAO,CAAC,EAAG;YACf,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC;QAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAmB,CAAC;QAErD,IAAI;YACF,IAAI,OAAO,CAAC,IAAI,IAAI,aAAa,EAAE;gBACjC,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,EAAG,CAAC,CAAC;aACvD;iBAAM,IAAI,OAAO,CAAC,IAAI,IAAI,aAAa,EAAE;gBACxC,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,EAAG,CAAC,CAAC;aACtD;iBAAM,IAAI,OAAO,CAAC,IAAI,IAAI,cAAc,EAAE;gBACzC,YAAY,CAAC,OAAO,CAAC,CAAC;aACvB;iBAAM,IAAI,OAAO,CAAC,IAAI,IAAI,aAAa,EAAE;gBACxC,WAAW,CAAC,OAAO,CAAC,CAAC;aACtB;iBAAM,IAAI,OAAO,CAAC,IAAI,IAAI,eAAe,EAAE;gBAC1C,4DAA4D;gBAC3D,IAAY,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;aAC9C;SACF;QAAC,OAAO,CAAC,EAAE;YACV,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACjB,WAAW,CAAC;gBACV,EAAE,EAAE,OAAO,CAAC,EAAG;gBACf,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,CAAC;aACR,CAAC,CAAC;SACJ;IACH,CAAC,CAAA;AACH,CAAC;AAxED,gCAwEC"} -------------------------------------------------------------------------------- /src/WorkerBee.ts: -------------------------------------------------------------------------------- 1 | import { WorkerMap } from "./quickWorkers"; 2 | import { workerCode } from "./workerCode"; 3 | 4 | 5 | export class WorkerBee { 6 | counter = 0; 7 | worker: Worker; 8 | 9 | constructor(init: {[key: string]: Function} = {}) { 10 | let workerString = `(${WorkerConfig.workerCode})(self)`; 11 | const blob = new Blob([workerString], { type: 'application/javascript' }); 12 | this.worker = new Worker(URL.createObjectURL(blob)); 13 | } 14 | 15 | sendMessage(message: WorkerBMessage): Promise { 16 | message.id = `${message.type}_${this.counter++}`; 17 | this.worker.postMessage(JSON.stringify(message)); 18 | 19 | return new Promise((resolve, reject) => { 20 | const listener = (e: MessageEvent) => { 21 | const response = JSON.parse(e.data) as WorkerBResponse; 22 | if (response.id === message.id) { 23 | 24 | this.worker.removeEventListener('message', listener); 25 | 26 | if (response.type == "failure") { 27 | reject(response.body); 28 | } else { 29 | resolve(response.body); 30 | } 31 | } 32 | }; 33 | this.worker.addEventListener('message', listener); 34 | }); 35 | } 36 | } 37 | 38 | /** Used so we can override in tests. */ 39 | export const WorkerConfig = { 40 | workerCode: workerCode.toString() 41 | } 42 | 43 | export type WorkerBResponse = { 44 | type: 'success' | 'failure', 45 | body: any, 46 | id: string 47 | } 48 | 49 | export type WorkerBMessage = { id?: string } 50 | & (SetProperty | CallFunctionMessage | ImportScriptsMessage | GetProperty | SetFunctionMessage); 51 | 52 | export type SetProperty = { 53 | type: 'setProperty'; 54 | name: string; 55 | value: any; 56 | } 57 | 58 | export type GetProperty = { 59 | type: 'getProperty'; 60 | name: string; 61 | } 62 | 63 | export type ImportScriptsMessage = { 64 | type: 'importScripts'; 65 | scripts: string[]; 66 | } 67 | 68 | export type CallFunctionMessage = { 69 | type: 'callFunction'; 70 | name: string; 71 | args: any[]; 72 | } 73 | 74 | export type SetFunctionMessage = { 75 | type: 'setFunction'; 76 | name: string; 77 | body: string; 78 | } 79 | -------------------------------------------------------------------------------- /src/workerCode.ts: -------------------------------------------------------------------------------- 1 | import { WorkerMap } from "./quickWorkers"; 2 | import { WorkerBResponse, WorkerBMessage, CallFunctionMessage, GetProperty } from "./WorkerBee"; 3 | 4 | export function workerCode(self: Worker){ 5 | const context: WorkerMap = {}; 6 | 7 | function sendMessage(message: WorkerBResponse) { 8 | self.postMessage(JSON.stringify(message)); 9 | } 10 | 11 | function setProperty(name: string, value: string, id: string) { 12 | context[name] = value; 13 | sendMessage({ 14 | id, 15 | type: "success", 16 | body: value 17 | }); 18 | } 19 | 20 | function setFunction(name: string, body: string, id: string) { 21 | let test = () => { }; 22 | eval(`test = ${body}`); 23 | 24 | context[name] = test; 25 | sendMessage({ 26 | id, 27 | type: "success", 28 | body: true 29 | }); 30 | } 31 | 32 | function callFunction(message: CallFunctionMessage & { id?: string }) { 33 | if (context[message.name] && context[message.name] instanceof Function) { 34 | const result = context[message.name].apply(context, message.args); 35 | sendMessage({ 36 | id: message.id!, 37 | type: 'success', 38 | body: result, 39 | }); 40 | } 41 | } 42 | 43 | function getProperty(message: GetProperty & { id?: string }) { 44 | sendMessage({ 45 | id: message.id!, 46 | type: 'success', 47 | body: context[message.name], 48 | }); 49 | } 50 | 51 | self.onmessage = function (e) { 52 | const message = JSON.parse(e.data) as WorkerBMessage; 53 | 54 | try { 55 | if (message.type == 'setProperty') { 56 | setProperty(message.name, message.value, message.id!); 57 | } else if (message.type == 'setFunction') { 58 | setFunction(message.name, message.body, message.id!); 59 | } else if (message.type == 'callFunction') { 60 | callFunction(message); 61 | } else if (message.type == 'getProperty') { 62 | getProperty(message); 63 | } else if (message.type == 'importScripts') { 64 | // TODO: Worker type should know about importScripts, right? 65 | (self as any).importScripts(message.scripts); 66 | } 67 | } catch (e) { 68 | console.error(e); 69 | sendMessage({ 70 | id: message.id!, 71 | type: 'failure', 72 | body: e, 73 | }); 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /dist/workerCode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.workerCode = void 0; 4 | function workerCode(self) { 5 | const context = {}; 6 | function sendMessage(message) { 7 | self.postMessage(JSON.stringify(message)); 8 | } 9 | function setProperty(name, value, id) { 10 | context[name] = value; 11 | sendMessage({ 12 | id, 13 | type: "success", 14 | body: value 15 | }); 16 | } 17 | function setFunction(name, body, id) { 18 | let test = () => { }; 19 | eval(`test = ${body}`); 20 | context[name] = test; 21 | sendMessage({ 22 | id, 23 | type: "success", 24 | body: true 25 | }); 26 | } 27 | function callFunction(message) { 28 | if (context[message.name] && context[message.name] instanceof Function) { 29 | const result = context[message.name].apply(context, message.args); 30 | sendMessage({ 31 | id: message.id, 32 | type: 'success', 33 | body: result, 34 | }); 35 | } 36 | } 37 | function getProperty(message) { 38 | sendMessage({ 39 | id: message.id, 40 | type: 'success', 41 | body: context[message.name], 42 | }); 43 | } 44 | self.onmessage = function (e) { 45 | const message = JSON.parse(e.data); 46 | try { 47 | if (message.type == 'setProperty') { 48 | setProperty(message.name, message.value, message.id); 49 | } 50 | else if (message.type == 'setFunction') { 51 | setFunction(message.name, message.body, message.id); 52 | } 53 | else if (message.type == 'callFunction') { 54 | callFunction(message); 55 | } 56 | else if (message.type == 'getProperty') { 57 | getProperty(message); 58 | } 59 | else if (message.type == 'importScripts') { 60 | // TODO: Worker type should know about importScripts, right? 61 | self.importScripts(message.scripts); 62 | } 63 | } 64 | catch (e) { 65 | console.error(e); 66 | sendMessage({ 67 | id: message.id, 68 | type: 'failure', 69 | body: e, 70 | }); 71 | } 72 | }; 73 | } 74 | exports.workerCode = workerCode; 75 | //# sourceMappingURL=workerCode.js.map -------------------------------------------------------------------------------- /dist/quickWorkers.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** Utils for easy use of WorkerBee workers */ 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | exports.createWorker = exports.miniWorker = void 0; 5 | const WorkerBee_1 = require("./WorkerBee"); 6 | function miniWorker(func) { 7 | const worker = createWorker({ 8 | mini: func 9 | }); 10 | return worker.mini; 11 | } 12 | exports.miniWorker = miniWorker; 13 | function createWorker(init) { 14 | const worker = new WorkerBee_1.WorkerBee(); 15 | const propertiesMap = {}; 16 | const setters = {}; 17 | for (const name in init) { 18 | // If it's a function, make a wrapper 19 | if (init[name] instanceof Function) { 20 | let propertyReady = worker.sendMessage({ 21 | type: 'setFunction', 22 | name, 23 | body: init[name].toString(), 24 | }); 25 | propertiesMap[name] = (async (...args) => { 26 | await propertyReady; 27 | return await worker.sendMessage({ 28 | type: 'callFunction', 29 | name: name, 30 | args 31 | }); 32 | }); 33 | } 34 | else { 35 | // if it's not, write an async getter/setter wrapper 36 | let propertyReady = worker.sendMessage({ 37 | type: 'setProperty', 38 | name, 39 | value: init[name], 40 | }); 41 | const setterName = `set${name[0].toUpperCase()}${name.slice(1)}`; 42 | const setterFunction = async (value) => { 43 | await propertyReady; 44 | return await worker.sendMessage({ 45 | type: 'setProperty', 46 | name, 47 | value 48 | }); 49 | }; 50 | setters[setterName] = setterFunction; 51 | Object.defineProperty(propertiesMap, name, { 52 | enumerable: true, 53 | get: async function () { 54 | await propertyReady; 55 | return await worker.sendMessage({ 56 | type: 'getProperty', 57 | name: name, 58 | }); 59 | } 60 | }); 61 | } 62 | } 63 | const utils = { 64 | destroy: () => worker.worker.terminate(), 65 | importScripts: async (scripts) => { 66 | return await worker.sendMessage({ 67 | type: 'importScripts', 68 | scripts 69 | }); 70 | } 71 | }; 72 | // Using Object.assign lets us keep the getters in propertiesMap. 73 | return Object.assign(propertiesMap, setters, utils); 74 | } 75 | exports.createWorker = createWorker; 76 | //# sourceMappingURL=quickWorkers.js.map -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WorkerBee 2 | 3 | WorkerBee is a minimal TypeScript library to make using Web Workers as easy as possible. 4 | Instead of having to create a seperate file for code that is run in a different thread, 5 | workers can be made inline with their own functions and be communicated with asynchronously. 6 | 7 | ## Why? 8 | 9 | Javascript is single-threaded. If a complex operation is running for a while, nothing else will 10 | run, and your browser will slow down, and your UI will become unresponsive. 11 | 12 | Here's a quick example of a function that can run for a long time if you're not careful: 13 | 14 | ```typescript 15 | // From https://stackoverflow.com/a/57012040: 16 | const findNthPrime = (n : number) => { 17 | const primes = []; 18 | let i = 1; 19 | while (i++ && primes.length < n) { 20 | primes.reduce((a,c)=>(i%c)*a,2) && prime.push(i); 21 | } 22 | return primes.pop(); 23 | } 24 | 25 | // Will likely cause your browser to become unresponsive: 26 | const result = findNthPrime(100000); 27 | ``` 28 | 29 | With WorkerBee - if you have a simple, self-contained function like that, you can execute 30 | it another thread like so: 31 | 32 | ```typescript 33 | const workerFindNthPrime = miniWorker(findNthPrime); 34 | const result = await workerFindNthPrime(100000); 35 | ``` 36 | 37 | And things will work nice and smoothly, no matter how long that operation takes. 38 | See below for more complex use-cases. 39 | 40 | ## Installation 41 | 42 | ``` 43 | npm install @lukeschaefer/worker-bee 44 | ``` 45 | 46 | ## Usage: 47 | 48 | There are two ways of creating workers in WorkerBee: 49 | 50 | ### `miniWorker()` 51 | 52 | Like we saw above, simply give miniWorker a self-contained function, and it can execute it in a separate thread: 53 | 54 | ```typescript 55 | import { miniWorker } from '@lukeschaefer/worker-bee'; 56 | 57 | const multiplier = miniWorker((x, y) => { 58 | return x * y; 59 | }); 60 | 61 | const result = await multiplier(12,3); 62 | console.log(result); // 36 is logged 63 | ``` 64 | 65 | But that's about all it can do. 66 | 67 | ### `createWorker` 68 | 69 | `createWorker` takes in an initialization object, which can consist of properties and methods to call. 70 | It will generate accessors and setters so that you can interact with your Web Worker as easily as a normal object: 71 | 72 | ```typescript 73 | 74 | const usefulWorker = createWorker({ 75 | counter: 0, 76 | addToCounter: function(input: number) { 77 | this.counter += input; 78 | return this.counter; 79 | }, 80 | addRandomToCounter: function() { 81 | return this.addToCounter(Math.random()); 82 | } 83 | }); 84 | 85 | usefulWorker.counter = 20; 86 | 87 | usefulWorker.addToCounter(10); // Promise<30> 88 | usefulWorker.addRandomToCounter(); // Promise<30.123> 89 | ``` 90 | 91 | `createWorker` workers come with a few helper functions as well: 92 | 93 | - `destroy()` - Closes the webworker. 94 | - `importScript()` - Makes the webworker call `loadScript` 95 | 96 | -------------------------------------------------------------------------------- /src/quickWorkers.ts: -------------------------------------------------------------------------------- 1 | /** Utils for easy use of WorkerBee workers */ 2 | 3 | import { WorkerBee } from "./WorkerBee"; 4 | 5 | export function miniWorker

(func: (P)): (...args: Parameters

) => Promise> { 6 | const worker = createWorker({ 7 | mini: func 8 | }); 9 | return worker.mini; 10 | } 11 | 12 | export function createWorker(init: T): WorkerInterface { 13 | const worker = new WorkerBee(); 14 | 15 | const propertiesMap = {} as WorkerProperties; 16 | const setters = {} as WorkerSetters; 17 | for (const name in init) { 18 | // If it's a function, make a wrapper 19 | if ((init[name] as any) instanceof Function) { 20 | let propertyReady = worker.sendMessage({ 21 | type: 'setFunction', 22 | name, 23 | body: init[name].toString(), 24 | }); 25 | 26 | propertiesMap[name] = (async (...args: any[]) => { 27 | await propertyReady; 28 | return await worker.sendMessage({ 29 | type: 'callFunction', 30 | name: name, 31 | args 32 | }); 33 | }) as ToWorkerProperty; 34 | } else { 35 | // if it's not, write an async getter/setter wrapper 36 | let propertyReady = worker.sendMessage({ 37 | type: 'setProperty', 38 | name, 39 | value: init[name], 40 | }); 41 | 42 | const setterName = `set${name[0].toUpperCase()}${name.slice(1)}` as SetterKey; 43 | const setterFunction = async (value: any) => { 44 | await propertyReady; 45 | return await worker.sendMessage({ 46 | type: 'setProperty', 47 | name, 48 | value 49 | }); 50 | }; 51 | setters[setterName] = setterFunction; 52 | 53 | Object.defineProperty(propertiesMap, name, { 54 | enumerable: true, 55 | get: async function () { 56 | await propertyReady; 57 | return await worker.sendMessage({ 58 | type: 'getProperty', 59 | name: name, 60 | }); 61 | } 62 | }); 63 | } 64 | } 65 | 66 | const utils: WorkerUtils = { 67 | destroy: () => worker.worker.terminate(), 68 | importScripts: async (scripts: string[]) => { 69 | return await worker.sendMessage({ 70 | type: 'importScripts', 71 | scripts 72 | }); 73 | } 74 | }; 75 | 76 | // Using Object.assign lets us keep the getters in propertiesMap. 77 | return Object.assign(propertiesMap, setters, utils); 78 | } 79 | 80 | export type WorkerUtils = { 81 | destroy: () => void; 82 | importScripts: (scripts: string[]) => Promise; 83 | } 84 | 85 | export type AnyFunction = (...args: any[]) => any; 86 | export type WorkerFunction = (...args: Parameters) => Promise>; 87 | 88 | export type ToWorkerProperty = T extends AnyFunction ? WorkerFunction : Promise; 89 | 90 | export type WorkerMap = { [key: string]: (any) }; 91 | export type WorkerProperties 92 | = { [key in keyof T]: ToWorkerProperty }; 93 | 94 | export type SetterKey = `set${Capitalize}` 95 | export type SetterFunc = (value: T) => Promise 96 | 97 | export type WorkerSetters = { 98 | [Key in keyof T & string as SetterKey]: SetterFunc }; 99 | 100 | 101 | export type WorkerInterface = WorkerProperties & WorkerSetters & WorkerUtils; -------------------------------------------------------------------------------- /dist/WorkerBee.test.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"WorkerBee.test.js","sourceRoot":"","sources":["../src/WorkerBee.test.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /src/WorkerBee.test.ts: -------------------------------------------------------------------------------- 1 | import { createWorker, WorkerMap } from "./quickWorkers"; 2 | import { WorkerConfig, WorkerBMessage, WorkerBResponse, CallFunctionMessage, GetProperty } from "./WorkerBee"; 3 | 4 | describe('createWorker workers', () => { 5 | 6 | beforeEach(() => { 7 | WorkerConfig.workerCode = swappedOutWorkerCode; 8 | }); 9 | 10 | it('can execute a standalone function', async () => { 11 | const worker = createWorker({ 12 | add: (a: number, b: number) => a + b, 13 | }); 14 | expect(await worker.add(1, 2)).toBe(3); 15 | }); 16 | 17 | it('can execute scoped functions', async () => { 18 | const worker = createWorker({ 19 | add: (a: number, b: number, c: number) => a + b + c, 20 | complexThing: function (a: number, b: number): number { 21 | return this.add(a, b, 3); 22 | } 23 | }); 24 | 25 | expect(await worker.complexThing(1, 2)).toBe(6); 26 | }); 27 | 28 | it('generates property getters', async () => { 29 | const worker = createWorker({ 30 | a: 123, 31 | b: 'abc', 32 | }); 33 | 34 | expect(await worker.a).toBe(123); 35 | expect(await worker.b).toBe('abc'); 36 | }); 37 | 38 | it('generates property setters', async () => { 39 | const worker = createWorker({ 40 | a: 1, 41 | b: 'abc', 42 | }); 43 | await worker.setA(2); 44 | await worker.setB('345'); 45 | 46 | expect(await worker.a).toBe(2); 47 | expect(await worker.b).toBe('345'); 48 | }); 49 | 50 | it('functions can access propeties', async () => { 51 | const worker = createWorker({ 52 | a: 123, 53 | b: 'abc', 54 | c: function () { 55 | return this.a + this.b.length; 56 | } 57 | }); 58 | 59 | expect(await worker.c()).toBe(126); 60 | }); 61 | }); 62 | 63 | // TODO: Not have to do this. Karma messes with the worker code, so we 64 | // manually replace it here. Another option would be to regex it out of the 65 | // WorkerB.ts source file? Because keeping these in parity is dumb. 66 | const swappedOutWorkerCode = ((self: Worker) => { 67 | const context: WorkerMap = {}; 68 | 69 | function sendMessage(message: WorkerBResponse) { 70 | self.postMessage(JSON.stringify(message)); 71 | } 72 | 73 | function setProperty(name: string, value: string, id: string) { 74 | context[name] = value; 75 | sendMessage({ 76 | id, 77 | type: "success", 78 | body: value 79 | }); 80 | } 81 | 82 | function setFunction(name: string, body: string, id: string) { 83 | let test = () => { }; 84 | eval(`test = ${body}`); 85 | 86 | context[name] = test; 87 | sendMessage({ 88 | id, 89 | type: "success", 90 | body: true 91 | }); 92 | } 93 | 94 | function callFunction(message: CallFunctionMessage & { id?: string }) { 95 | if (context[message.name] && context[message.name] instanceof Function) { 96 | const result = context[message.name].apply(context, message.args); 97 | sendMessage({ 98 | id: message.id!, 99 | type: 'success', 100 | body: result, 101 | }); 102 | } 103 | } 104 | 105 | function getProperty(message: GetProperty & { id?: string }) { 106 | sendMessage({ 107 | id: message.id!, 108 | type: 'success', 109 | body: context[message.name], 110 | }); 111 | } 112 | 113 | self.onmessage = function (e) { 114 | const message = JSON.parse(e.data) as WorkerBMessage; 115 | 116 | try { 117 | if (message.type == 'setProperty') { 118 | setProperty(message.name, message.value, message.id!); 119 | } else if (message.type == 'setFunction') { 120 | setFunction(message.name, message.body, message.id!); 121 | } else if (message.type == 'callFunction') { 122 | callFunction(message); 123 | } else if (message.type == 'getProperty') { 124 | getProperty(message); 125 | } else if (message.type == 'importScripts') { 126 | // TODO: Worker type should know about importScripts, right? 127 | (self as any).importScripts(message.scripts); 128 | } 129 | } catch (e) { 130 | console.error(e); 131 | sendMessage({ 132 | id: message.id!, 133 | type: 'failure', 134 | body: e, 135 | }); 136 | } 137 | } 138 | }).toString() -------------------------------------------------------------------------------- /dist/WorkerBee.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const quickWorkers_1 = require("./quickWorkers"); 4 | const WorkerBee_1 = require("./WorkerBee"); 5 | describe('createWorker workers', () => { 6 | beforeEach(() => { 7 | WorkerBee_1.WorkerConfig.workerCode = swappedOutWorkerCode; 8 | }); 9 | it('can execute a standalone function', async () => { 10 | const worker = (0, quickWorkers_1.createWorker)({ 11 | add: (a, b) => a + b, 12 | }); 13 | expect(await worker.add(1, 2)).toBe(3); 14 | }); 15 | it('can execute scoped functions', async () => { 16 | const worker = (0, quickWorkers_1.createWorker)({ 17 | add: (a, b, c) => a + b + c, 18 | complexThing: function (a, b) { 19 | return this.add(a, b, 3); 20 | } 21 | }); 22 | expect(await worker.complexThing(1, 2)).toBe(6); 23 | }); 24 | it('generates property getters', async () => { 25 | const worker = (0, quickWorkers_1.createWorker)({ 26 | a: 123, 27 | b: 'abc', 28 | }); 29 | expect(await worker.a).toBe(123); 30 | expect(await worker.b).toBe('abc'); 31 | }); 32 | it('generates property setters', async () => { 33 | const worker = (0, quickWorkers_1.createWorker)({ 34 | a: 1, 35 | b: 'abc', 36 | }); 37 | await worker.setA(2); 38 | await worker.setB('345'); 39 | expect(await worker.a).toBe(2); 40 | expect(await worker.b).toBe('345'); 41 | }); 42 | it('functions can access propeties', async () => { 43 | const worker = (0, quickWorkers_1.createWorker)({ 44 | a: 123, 45 | b: 'abc', 46 | c: function () { 47 | return this.a + this.b.length; 48 | } 49 | }); 50 | expect(await worker.c()).toBe(126); 51 | }); 52 | }); 53 | // TODO: Not have to do this. Karma messes with the worker code, so we 54 | // manually replace it here. Another option would be to regex it out of the 55 | // WorkerB.ts source file? Because keeping these in parity is dumb. 56 | const swappedOutWorkerCode = ((self) => { 57 | const context = {}; 58 | function sendMessage(message) { 59 | self.postMessage(JSON.stringify(message)); 60 | } 61 | function setProperty(name, value, id) { 62 | context[name] = value; 63 | sendMessage({ 64 | id, 65 | type: "success", 66 | body: value 67 | }); 68 | } 69 | function setFunction(name, body, id) { 70 | let test = () => { }; 71 | eval(`test = ${body}`); 72 | context[name] = test; 73 | sendMessage({ 74 | id, 75 | type: "success", 76 | body: true 77 | }); 78 | } 79 | function callFunction(message) { 80 | if (context[message.name] && context[message.name] instanceof Function) { 81 | const result = context[message.name].apply(context, message.args); 82 | sendMessage({ 83 | id: message.id, 84 | type: 'success', 85 | body: result, 86 | }); 87 | } 88 | } 89 | function getProperty(message) { 90 | sendMessage({ 91 | id: message.id, 92 | type: 'success', 93 | body: context[message.name], 94 | }); 95 | } 96 | self.onmessage = function (e) { 97 | const message = JSON.parse(e.data); 98 | try { 99 | if (message.type == 'setProperty') { 100 | setProperty(message.name, message.value, message.id); 101 | } 102 | else if (message.type == 'setFunction') { 103 | setFunction(message.name, message.body, message.id); 104 | } 105 | else if (message.type == 'callFunction') { 106 | callFunction(message); 107 | } 108 | else if (message.type == 'getProperty') { 109 | getProperty(message); 110 | } 111 | else if (message.type == 'importScripts') { 112 | // TODO: Worker type should know about importScripts, right? 113 | self.importScripts(message.scripts); 114 | } 115 | } 116 | catch (e) { 117 | console.error(e); 118 | sendMessage({ 119 | id: message.id, 120 | type: 'failure', 121 | body: e, 122 | }); 123 | } 124 | }; 125 | }).toString(); 126 | //# sourceMappingURL=WorkerBee.test.js.map --------------------------------------------------------------------------------