-
Notifications
You must be signed in to change notification settings - Fork 91
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 93bdc8b
Showing
5 changed files
with
200 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
root = true | ||
|
||
[*] | ||
indent_style = tab | ||
end_of_line = lf | ||
charset = utf-8 | ||
trim_trailing_whitespace = true | ||
insert_final_newline = true | ||
|
||
[{package.json,.*rc,*.yml}] | ||
indent_style = space | ||
indent_size = 2 | ||
|
||
[*.md] | ||
trim_trailing_whitespace = false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
node_modules | ||
dist | ||
build | ||
.DS_Store | ||
package-lock.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<p align="center"> | ||
<img src="https://i.imgur.com/JLAwk0S.png" width="300" height="300" alt="workerize-loader"> | ||
<br> | ||
<a href="https://www.npmjs.org/package/workerize"><img src="https://img.shields.io/npm/v/workerize.svg?style=flat" alt="npm"></a> <a href="https://travis-ci.org/developit/workerize"><img src="https://travis-ci.org/developit/workerize.svg?branch=master" alt="travis"></a> | ||
</p> | ||
|
||
# Workerize | ||
|
||
**NOTE:** If you're using Webpack, try [workerize-loader](https://github.com/developit/workerize-loader). | ||
|
||
> Moves a module into a Web Worker, automatically reflecting exported functions as asynchronous proxies. | ||
- Bundles a tiny, purpose-built RPC implementation into your app | ||
- If exported module methods are already async, signature is unchanged | ||
- Supports synchronous and asynchronous worker functions | ||
- Works beautifully with async/await | ||
|
||
|
||
## Install | ||
|
||
```sh | ||
npm install --save workerize | ||
``` | ||
|
||
|
||
### Usage | ||
|
||
**worker.js**: | ||
|
||
```js | ||
let worker = workerize(` | ||
export function add(a, b) { | ||
// block for half a second to demonstrate asynchronicity | ||
let start = Date.now(); | ||
while (Date.now()-start < 250); | ||
return a + b; | ||
} | ||
`); | ||
|
||
(async () => { | ||
console.log('3 + 9 = ', await worker.add(3, 9)); | ||
console.log('1 + 2 = ', await worker.add(1, 2)); | ||
})(); | ||
``` | ||
|
||
### License | ||
|
||
[MIT License](LICENSE.md) © [Jason Miller](https://jasonformat.com) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"name": "workerize", | ||
"version": "0.1.0", | ||
"description": "Run a module into a Web Worker.", | ||
"main": "dist/workerize.js", | ||
"module": "src/index.js", | ||
"scripts": { | ||
"build": "microbundle", | ||
"prepublishOnly": "npm run build", | ||
"test": "echo \"Error: no test specified\" && exit 0" | ||
}, | ||
"keywords": [], | ||
"author": "Jason Miller <jason@developit.ca> (http://jasonformat.com)", | ||
"license": "ISC", | ||
"devDependencies": { | ||
"microbundle": "^0.2.4" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
|
||
/** TODO: | ||
* - pooling (+ load balancing by tracking # of open calls) | ||
* - queueing (worth it? sortof free via postMessage already) | ||
* | ||
* @example | ||
* let worker = workerize(` | ||
* export function add(a, b) { | ||
* // block for half a second to demonstrate asynchronicity | ||
* let start = Date.now(); | ||
* while (Date.now()-start < 250); | ||
* return a + b; | ||
* } | ||
* `); | ||
* (async () => { | ||
* console.log('3 + 9 = ', await worker.add(3, 9)); | ||
* console.log('1 + 2 = ', await worker.add(1, 2)); | ||
* })(); | ||
*/ | ||
|
||
|
||
export default function workerize(code) { | ||
let exports = {}; | ||
let exportsObjName = `__EXPORTS_${Math.random().toString().substring(2)}__`; | ||
if (typeof code==='function') code = `(${toCode(code)})(${exportsObjName})`; | ||
code = toCjs(code, exportsObjName, exports); | ||
code += `\n(${toCode(setup)})(self, ${exportsObjName}, {})`; | ||
let blob = new Blob([code], { | ||
type: 'application/javascript' | ||
}), | ||
url = URL.createObjectURL(blob), | ||
worker = new Worker(url), | ||
counter = 0, | ||
callbacks = {}; | ||
worker.kill = signal => { | ||
worker.postMessage({ type: 'KILL', signal }); | ||
setTimeout(worker.terminate); | ||
}; | ||
let term = worker.terminate; | ||
worker.terminate = () => { | ||
URL.revokeObjectURL(url); | ||
worker.terminate(); | ||
}; | ||
worker.rpcMethods = {}; | ||
function setup(ctx, rpcMethods, callbacks) { | ||
/* | ||
ctx.expose = (methods, replace) => { | ||
if (typeof methods==='string') { | ||
rpcMethods[methods] = replace; | ||
} | ||
else { | ||
if (replace===true) rpcMethods = {}; | ||
Object.assign(rpcMethods, methods); | ||
} | ||
}; | ||
*/ | ||
ctx.addEventListener('message', ({ data }) => { | ||
if (data.type==='RPC') { | ||
let id = data.id; | ||
if (id!=null) { | ||
if (data.method) { | ||
let method = rpcMethods[data.method]; | ||
if (method==null) { | ||
ctx.postMessage({ type: 'RPC', id, error: 'NO_SUCH_METHOD' }); | ||
} | ||
else { | ||
Promise.resolve() | ||
.then( () => method.apply(null, data.params) ) | ||
.then( result => { ctx.postMessage({ type: 'RPC', id, result }); }) | ||
.catch( error => { ctx.postMessage({ type: 'RPC', id, error }); }); | ||
} | ||
} | ||
else { | ||
let callback = callbacks[id]; | ||
if (callback==null) throw Error(`Unknown callback ${id}`); | ||
delete callbacks[id]; | ||
if (data.error) callback.reject(Error(data.error)); | ||
else callback.resolve(data.result); | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
setup(worker, worker.rpcMethods, callbacks); | ||
worker.call = (method, params) => new Promise( (resolve, reject) => { | ||
let id = `rpc${++counter}`; | ||
callbacks[id] = { method, resolve, reject }; | ||
worker.postMessage({ type: 'RPC', id, method, params }); | ||
}); | ||
for (let i in exports) { | ||
if (exports.hasOwnProperty(i) && !(i in worker)) { | ||
worker[i] = (...args) => worker.call(i, args); | ||
} | ||
} | ||
return worker; | ||
} | ||
|
||
function toCode(func) { | ||
return Function.prototype.toString.call(func); | ||
This comment has been minimized.
Sorry, something went wrong. |
||
} | ||
|
||
function toCjs(code, exportsObjName, exports) { | ||
exportsObjName = exportsObjName || 'exports'; | ||
exports = exports || {}; | ||
code = code.replace(/^(\s*)export\s+default\s+/m, (s, before) => { | ||
exports.default = true; | ||
return `${before}${exportsObjName}.default = `; | ||
}); | ||
code = code.replace(/^(\s*)export\s+(function|const|let|var)(\s+)([a-zA-Z$_][a-zA-Z0-9$_]*)/m, (s, before, type, ws, name) => { | ||
exports[name] = true; | ||
return `${before}${exportsObjName}.${name} = ${type}${ws}${name}`; | ||
}); | ||
return `var ${exportsObjName} = {};\n${code}\n${exportsObjName};`; | ||
} |
Is there a difference in calling
Function.prototype.toString.call(func)
vs.func.toString()
?