11/** Move an async function into its own thread.
22 * @param {Function } asyncFunction An (async) function to run in a Worker.
3+ * @public
34 */
45export default function greenlet ( asyncFunction ) {
5- // Create an "inline" worker
6+ // Create an "inline" worker (1:1 at definition time)
67 let worker = new Worker (
78 // The URL is a pointer to a stringified function (as a blob object)
89 URL . createObjectURL (
910 new Blob ( [
1011 // Register our wrapper function as the message handler
11- 'onmessage=(' + (
12- // f() is the user-supplied async function
13- userFunc => ( { data } ) => Promise . resolve ( ) . then (
14- // invoking within then() captures exceptions in f() as rejections
15- ( ) => userFunc . apply ( userFunc , data [ 1 ] )
16- ) . then (
17- d => {
18- // success handler - callback(id, null, result)
19- postMessage ( [ data [ 0 ] , null , d ] ) ;
20- } ,
21- e => {
22- // error handler - callback(id, err)
23- postMessage ( [ data [ 0 ] , '' + e ] ) ;
24- }
25- )
26- ) + ')(' + asyncFunction + ')' // pass user-supplied function to the closure
12+ 'onmessage=(' + (
13+ // userFunc() is the user-supplied async function
14+ userFunc => e => {
15+ // Invoking within then() captures exceptions in userFunc() as rejections
16+ Promise . resolve ( e . data [ 1 ] ) . then (
17+ userFunc . apply . bind ( userFunc , userFunc )
18+ ) . then (
19+ // success handler - callback(id, SUCCESS(0), result)
20+ d => { postMessage ( [ e . data [ 0 ] , 0 , d ] ) ; } ,
21+ // error handler - callback(id, ERROR(1), error)
22+ e => { postMessage ( [ e . data [ 0 ] , 1 , '' + e ] ) ; }
23+ ) ;
24+ }
25+ ) + ')(' + asyncFunction + ')' // pass user-supplied function to the closure
2726 ] )
2827 )
2928 ) ,
@@ -34,18 +33,29 @@ export default function greenlet(asyncFunction) {
3433 // Outward-facing promises store their "controllers" (`[request, reject]`) here:
3534 promises = { } ;
3635
37- // Handle RPC results/errors coming back out of the worker
38- worker . onmessage = ( { data : [ id , err , result ] } ) => {
36+ /** Handle RPC results/errors coming back out of the worker.
37+ * Messages coming from the worker take the form `[id, status, result]`:
38+ * id - counter-based unique ID for the RPC call
39+ * status - 0 for success, 1 for failure
40+ * result - the result or error, depending on `status`
41+ */
42+ worker . onmessage = e => {
3943 // invoke the promise's resolve() or reject() depending on whether there was an error.
40- promises [ id ] [ err ? 1 : 0 ] ( err || result ) ;
44+ promises [ e . data [ 0 ] ] [ e . data [ 1 ] ] ( e . data [ 2 ] ) ;
45+
4146 // ... then delete the promise controller
42- delete promises [ id ] ;
47+ promises [ e . data [ 0 ] ] = null ;
4348 } ;
4449
4550 // Return a proxy function that forwards calls to the worker & returns a promise for the result.
46- return ( ...args ) => new Promise ( ( resolve , reject ) => {
47- promises [ ++ currentId ] = [ resolve , reject ] ;
48- // Send an RPC call to the worker - call(id, params)
49- worker . postMessage ( [ currentId , args ] ) ;
50- } ) ;
51+ return function ( args ) {
52+ args = [ ] . slice . call ( arguments ) ;
53+ return new Promise ( function ( ) {
54+ // Add the promise controller to the registry
55+ promises [ ++ currentId ] = arguments ;
56+
57+ // Send an RPC call to the worker - call(id, params)
58+ worker . postMessage ( [ currentId , args ] ) ;
59+ } ) ;
60+ } ;
5161}
0 commit comments