Skip to content

Commit 2e95131

Browse files
committed
generator and async generator support
fixes developit#38 First thing to note about this is that I broke the microbundle build. I'll attempt to fix the generator function to not use things microbundle doesn't expect, but I'm not sure how far I'll get with that. Which that's also something tying up this pull request: developit/greenlet#50 One other thing to note about this is that I decided on returning a promise of the asyncIterator from the generator function. This is due to the fact that the function from the user only ever gets evaluated as an executable function on the worker side.
1 parent 540ec5d commit 2e95131

1 file changed

Lines changed: 44 additions & 6 deletions

File tree

src/index.js

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,30 @@ export default function workerize(code, options) {
3636
URL.revokeObjectURL(url);
3737
term.call(worker);
3838
};
39-
worker.call = (method, params) => new Promise( (resolve, reject) => {
39+
worker.call = (method, params, genStatus=0, genId=undefined) => new Promise( (resolve, reject) => {
4040
let id = `rpc${++counter}`;
4141
callbacks[id] = [resolve, reject];
42-
worker.postMessage({ type: 'RPC', id, method, params });
43-
});
42+
worker.postMessage({ type: 'RPC', id, genId, method, genStatus, params });
43+
}).then(d => !d.hasOwnProperty("genId") ? d : (async function* workerAsyncGenPassthrough () {
44+
const genId = d.genId;
45+
try {
46+
let result = {done: false};
47+
let value;
48+
while (!result.done) {
49+
result = await worker.call(method, [value], 0, genId);
50+
if (result.done) { break; }
51+
value = yield result.value;
52+
}
53+
return result.value;
54+
}
55+
catch (err) {
56+
await worker.call(method, ['' + err], 2, genId);
57+
throw err;
58+
}
59+
finally {
60+
await worker.call(method, [undefined], 1, genId);
61+
}
62+
})());
4463
worker.rpcMethods = {};
4564
setup(worker, worker.rpcMethods, callbacks);
4665
worker.expose = methodName => {
@@ -51,10 +70,13 @@ export default function workerize(code, options) {
5170
for (i in exports) if (!(i in worker)) worker.expose(i);
5271
return worker;
5372
}
54-
5573
function setup(ctx, rpcMethods, callbacks) {
74+
let gencounter = 0;
75+
let GENS = {};
5676
ctx.addEventListener('message', ({ data }) => {
5777
let id = data.id;
78+
let genId = data.genId;
79+
let genStatus = data.genStatus;
5880
if (data.type!=='RPC' || id==null) return;
5981
if (data.method) {
6082
let method = rpcMethods[data.method];
@@ -63,15 +85,31 @@ function setup(ctx, rpcMethods, callbacks) {
6385
}
6486
else {
6587
Promise.resolve()
66-
.then( () => method.apply(null, data.params) )
67-
.then( result => { ctx.postMessage({ type: 'RPC', id, result }); })
88+
// Either use a generator or call a method.
89+
.then( () => !GENS[genId] ? method.apply(null, data.params) : GENS[genId][genStatus](data.params[0]))
90+
.then( result => {
91+
if (method.constructor.name === 'AsyncGeneratorFunction' || method.constructor.name === 'GeneratorFunction') {
92+
if (!GENS[genId]) {
93+
GENS[++gencounter] = [result.next.bind(result), result.return.bind(result), result.throw.bind(result)];
94+
// return an initial message of success.
95+
// genId should only be sent to the main thread when initializing the generator
96+
return ctx.postMessage({ type: 'RPC', id, genId: gencounter, result: { value: undefined, done: false } });
97+
}
98+
}
99+
ctx.postMessage({ type: 'RPC', id, result });
100+
if (result.done) {
101+
GENS[genId] = null;
102+
}
103+
})
68104
.catch( err => { ctx.postMessage({ type: 'RPC', id, error: ''+err }); });
69105
}
70106
}
71107
else {
72108
let callback = callbacks[id];
73109
if (callback==null) throw Error(`Unknown callback ${id}`);
74110
delete callbacks[id];
111+
// genId should only be sent to the main thread when initializing the generator
112+
if(data.genId) { data.result.genId = data.genId; }
75113
if (data.error) callback[1](Error(data.error));
76114
else callback[0](data.result);
77115
}

0 commit comments

Comments
 (0)