greenlet
Advanced tools
Comparing version 0.1.2 to 1.0.0
@@ -1,2 +0,2 @@ | ||
module.exports=function(e){var n=new Worker(URL.createObjectURL(new Blob(["onmessage=("+function(e){return function(n){var t=n.data;return Promise.resolve().then(function(){return e.apply(e,t[1])}).then(function(e){postMessage([t[0],null,e])},function(e){postMessage([t[0],""+e])})}}+")("+e+")"]))),t=0,o={};return n.onmessage=function(e){var n=e.data,t=n[0],r=n[1];o[t][r?1:0](r||n[2]),delete o[t]},function(){for(var e=[],r=arguments.length;r--;)e[r]=arguments[r];return new Promise(function(r,s){o[++t]=[r,s],n.postMessage([t,e])})}}; | ||
module.exports=function(e){var n=0,t={},a=new Worker("data:,$$="+e+";onmessage="+function(e){Promise.resolve().then($$.bind.apply($$,e.data)).then(function(n){postMessage([e.data[0],0,n],[n].filter(function(e){return e instanceof ArrayBuffer||e instanceof MessagePort||e instanceof ImageBitmap}))},function(n){postMessage([e.data[0],1,""+n])})});return a.onmessage=function(e){t[e.data[0]][e.data[1]](e.data[2]),t[e.data[0]]=null},function(e){return e=[].slice.call(arguments),new Promise(function(){t[++n]=arguments,a.postMessage([n,e],e.filter(function(e){return e instanceof ArrayBuffer||e instanceof MessagePort||e instanceof ImageBitmap}))})}}; | ||
//# sourceMappingURL=greenlet.js.map |
@@ -1,2 +0,2 @@ | ||
export default function(e){var n=new Worker(URL.createObjectURL(new Blob(["onmessage=("+function(e){return function(n){var t=n.data;return Promise.resolve().then(function(){return e.apply(e,t[1])}).then(function(e){postMessage([t[0],null,e])},function(e){postMessage([t[0],""+e])})}}+")("+e+")"]))),t=0,r={};return n.onmessage=function(e){var n=e.data,t=n[0],o=n[1];r[t][o?1:0](o||n[2]),delete r[t]},function(){for(var e=[],o=arguments.length;o--;)e[o]=arguments[o];return new Promise(function(o,a){r[++t]=[o,a],n.postMessage([t,e])})}}; | ||
export default function(e){var n=0,t={},a=new Worker("data:,$$="+e+";onmessage="+function(e){Promise.resolve().then($$.bind.apply($$,e.data)).then(function(n){postMessage([e.data[0],0,n],[n].filter(function(e){return e instanceof ArrayBuffer||e instanceof MessagePort||e instanceof ImageBitmap}))},function(n){postMessage([e.data[0],1,""+n])})});return a.onmessage=function(e){t[e.data[0]][e.data[1]](e.data[2]),t[e.data[0]]=null},function(e){return e=[].slice.call(arguments),new Promise(function(){t[++n]=arguments,a.postMessage([n,e],e.filter(function(e){return e instanceof ArrayBuffer||e instanceof MessagePort||e instanceof ImageBitmap}))})}}; | ||
//# sourceMappingURL=greenlet.m.js.map |
@@ -1,2 +0,2 @@ | ||
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):e.greenlet=n()}(this,function(){return function(e){var n=new Worker(URL.createObjectURL(new Blob(["onmessage=("+function(e){return function(n){var t=n.data;return Promise.resolve().then(function(){return e.apply(e,t[1])}).then(function(e){postMessage([t[0],null,e])},function(e){postMessage([t[0],""+e])})}}+")("+e+")"]))),t=0,o={};return n.onmessage=function(e){var n=e.data,t=n[0],r=n[1];o[t][r?1:0](r||n[2]),delete o[t]},function(){for(var e=[],r=arguments.length;r--;)e[r]=arguments[r];return new Promise(function(r,u){o[++t]=[r,u],n.postMessage([t,e])})}}}); | ||
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):e.greenlet=n()}(this,function(){return function(e){var n=0,t={},a=new Worker("data:,$$="+e+";onmessage="+function(e){Promise.resolve().then($$.bind.apply($$,e.data)).then(function(n){postMessage([e.data[0],0,n],[n].filter(function(e){return e instanceof ArrayBuffer||e instanceof MessagePort||e instanceof ImageBitmap}))},function(n){postMessage([e.data[0],1,""+n])})});return a.onmessage=function(e){t[e.data[0]][e.data[1]](e.data[2]),t[e.data[0]]=null},function(e){return e=[].slice.call(arguments),new Promise(function(){t[++n]=arguments,a.postMessage([n,e],e.filter(function(e){return e instanceof ArrayBuffer||e instanceof MessagePort||e instanceof ImageBitmap}))})}}}); | ||
//# sourceMappingURL=greenlet.umd.js.map |
/** Move an async function into its own thread. | ||
* @param {Function} fn The (async) function to run in a Worker. | ||
* @param {Function} asyncFunction An (async) function to run in a Worker. | ||
* @public | ||
*/ | ||
export default function greenlet(fn) { | ||
let w = new Worker(URL.createObjectURL(new Blob([ | ||
'onmessage=('+( | ||
f => ({ data }) => Promise.resolve().then( | ||
() => f.apply(f, data[1]) | ||
).then( | ||
d => { postMessage([data[0], null, d]); }, | ||
e => { postMessage([data[0], ''+e]); } | ||
) | ||
)+')('+fn+')' | ||
]))), | ||
c = 0, | ||
p = {}; | ||
w.onmessage = ({ data: [c,e,d] }) => { | ||
p[c][e?1:0](e||d); | ||
delete p[c]; | ||
export default function greenlet(asyncFunction) { | ||
// A simple counter is used to generate worker-global unique ID's for RPC: | ||
let currentId = 0; | ||
// Outward-facing promises store their "controllers" (`[request, reject]`) here: | ||
const promises = {}; | ||
// Create an "inline" worker (1:1 at definition time) | ||
const worker = new Worker( | ||
// Use a data URI for the worker's src. It inlines the target function and an RPC handler: | ||
'data:,$$='+asyncFunction+';onmessage='+(e => { | ||
/* global $$ */ | ||
// Invoking within then() captures exceptions in the supplied async function as rejections | ||
Promise.resolve().then( | ||
$$.bind.apply($$, e.data) | ||
).then( | ||
// success handler - callback(id, SUCCESS(0), result) | ||
// if `d` is transferable transfer zero-copy | ||
d => { | ||
postMessage([e.data[0], 0, d], [d].filter(x => ( | ||
(x instanceof ArrayBuffer) || | ||
(x instanceof MessagePort) || | ||
(x instanceof ImageBitmap) | ||
))); | ||
}, | ||
// error handler - callback(id, ERROR(1), error) | ||
er => { postMessage([e.data[0], 1, '' + er]); } | ||
); | ||
}) | ||
); | ||
/** Handle RPC results/errors coming back out of the worker. | ||
* Messages coming from the worker take the form `[id, status, result]`: | ||
* id - counter-based unique ID for the RPC call | ||
* status - 0 for success, 1 for failure | ||
* result - the result or error, depending on `status` | ||
*/ | ||
worker.onmessage = e => { | ||
// invoke the promise's resolve() or reject() depending on whether there was an error. | ||
promises[e.data[0]][e.data[1]](e.data[2]); | ||
// ... then delete the promise controller | ||
promises[e.data[0]] = null; | ||
}; | ||
return (...a) => new Promise( (y, n) => { | ||
p[++c] = [y, n]; | ||
w.postMessage([c, a]); | ||
}); | ||
} | ||
// Return a proxy function that forwards calls to the worker & returns a promise for the result. | ||
return function (args) { | ||
args = [].slice.call(arguments); | ||
return new Promise(function () { | ||
// Add the promise controller to the registry | ||
promises[++currentId] = arguments; | ||
// Send an RPC call to the worker - call(id, params) | ||
// The filter is to provide a list of transferables to send zero-copy | ||
worker.postMessage([currentId, args], args.filter(x => ( | ||
(x instanceof ArrayBuffer) || | ||
(x instanceof MessagePort) || | ||
(x instanceof ImageBitmap) | ||
))); | ||
}); | ||
}; | ||
} |
{ | ||
"name": "greenlet", | ||
"version": "0.1.2", | ||
"version": "1.0.0", | ||
"description": "Move an async function into its own thread.", | ||
@@ -8,2 +8,3 @@ "source": "greenlet.js", | ||
"module": "dist/greenlet.m.js", | ||
"types": "./index.d.ts", | ||
"scripts": { | ||
@@ -15,6 +16,11 @@ "prepare": "microbundle", | ||
"eslintConfig": { | ||
"extends": "eslint-config-developit" | ||
"extends": "eslint-config-developit", | ||
"rules": { | ||
"prefer-spread": 0, | ||
"prefer-rest-params": 0 | ||
} | ||
}, | ||
"files": [ | ||
"greenlet.js", | ||
"index.d.ts", | ||
"dist" | ||
@@ -21,0 +27,0 @@ ], |
@@ -49,5 +49,28 @@ <p align="center"> | ||
[🔄 **Run this example on JSFiddle**](https://jsfiddle.net/developit/mf9fbma5/) | ||
## License | ||
[MIT](https://oss.ninja/mit/developit) | ||
## Transferable ready | ||
Greenlet will even accept and optimize [transferables](https://developer.mozilla.org/en-US/docs/Web/API/Transferable) as arguments to and from a greenlet worker function. | ||
## Browser support | ||
Thankfully, Web Workers have been around for a while and [are broadly supported](https://caniuse.com/#feat=webworkers) by Chrome, Firefox, Safari, Edge, and Internet Explorer 10+. | ||
If you still need to support older browsers, you can just check for the presence of `window.Worker`: | ||
```js | ||
if (window.Worker) { | ||
... | ||
} else { | ||
... | ||
} | ||
``` | ||
## License & Credits | ||
> In addition to the contributors, credit goes to [@sgb-io](https://github.com/sgb-io) for his annotated exploration of Greenlet's source. This prompted a refactor that clarified the code and allowed for further size optimizations. | ||
[MIT License](https://oss.ninja/mit/developit) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
18874
10
74
0
76