Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

generator and async generator support #39

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 4 additions & 4 deletions loader.js
Expand Up @@ -2,8 +2,8 @@ try {
module.exports = require('workerize-loader');
}
catch (e) {
console.warn("Warning: workerize-loader is not installed.");
console.warn('Warning: workerize-loader is not installed.');
module.exports = function() {
throw "To use workerize as a loader, you must install workerize-loader.";
}
}
throw 'To use workerize as a loader, you must install workerize-loader.';
};
}
6 changes: 4 additions & 2 deletions package.json
Expand Up @@ -11,7 +11,7 @@
"build": "microbundle",
"prepublishOnly": "npm run build",
"release": "npm t && git commit -am $npm_package_version && git tag $npm_package_version && git push && git push --tags && npm publish",
"test": "echo \"Error: no test specified\" && exit 0"
"test": "eslint *.js && npm run -s build && karmatic --no-coverage"
},
"eslintConfig": {
"extends": "eslint-config-developit",
Expand All @@ -35,6 +35,8 @@
"devDependencies": {
"eslint": "^4.16.0",
"eslint-config-developit": "^1.1.1",
"microbundle": "^0.4.3"
"microbundle": "^0.4.3",
"karmatic": "^1.4.0",
"webpack": "^4.29.6"
}
}
58 changes: 53 additions & 5 deletions src/index.js
Expand Up @@ -36,10 +36,38 @@ export default function workerize(code, options) {
URL.revokeObjectURL(url);
term.call(worker);
};
worker.call = (method, params) => new Promise( (resolve, reject) => {
worker.call = (method, params, genStatus=0, genId=undefined) => new Promise( (resolve, reject) => {
let id = `rpc${++counter}`;
callbacks[id] = [resolve, reject];
worker.postMessage({ type: 'RPC', id, method, params });
worker.postMessage({ type: 'RPC', id, genId, method, genStatus, params });
}).then((d) => {
if (!d.hasOwnProperty('genId')) {
return d;
}
return (() => {
const genId = d.genId;
return {
done: false,
async next (value) {
if (this.done) { return { value: undefined, done: true }; }
const result = await worker.call(method, [value], 0, genId);
if (result.done) { return this.return(result.value); }
return result;
},
async return (value) {
await worker.call(method, [value], 1, genId);
this.done = true;
return { value, done: true };
},
async throw (err) {
await worker.call(method, [err], 0, genId);
throw err;
},
[Symbol.asyncIterator] () {
return this;
}
};
})();
});
worker.rpcMethods = {};
setup(worker, worker.rpcMethods, callbacks);
Expand All @@ -51,10 +79,13 @@ export default function workerize(code, options) {
for (i in exports) if (!(i in worker)) worker.expose(i);
return worker;
}

function setup(ctx, rpcMethods, callbacks) {
let gencounter = 0;
let GENS = {};
ctx.addEventListener('message', ({ data }) => {
let id = data.id;
let genId = data.genId;
let genStatus = data.genStatus;
if (data.type!=='RPC' || id==null) return;
if (data.method) {
let method = rpcMethods[data.method];
Expand All @@ -63,16 +94,33 @@ function setup(ctx, rpcMethods, callbacks) {
}
else {
Promise.resolve()
.then( () => method.apply(null, data.params) )
.then( result => { ctx.postMessage({ type: 'RPC', id, result }); })
// Either use a generator or call a method.
.then( () => !GENS[genId] ? method.apply(null, data.params) : GENS[genId][genStatus](data.params[0]))
.then( result => {
if (method.constructor.name === 'AsyncGeneratorFunction' || method.constructor.name === 'GeneratorFunction') {
if (!GENS[genId]) {
GENS[++gencounter] = [result.next.bind(result), result.return.bind(result), result.throw.bind(result)];
// return an initial message of success.
// genId should only be sent to the main thread when initializing the generator
return ctx.postMessage({ type: 'RPC', id, genId: gencounter, result: { value: undefined, done: false } });
}
}
ctx.postMessage({ type: 'RPC', id, result });
if (result.done) {
GENS[genId] = null;
}
})
.catch( err => { ctx.postMessage({ type: 'RPC', id, error: ''+err }); });
}
}
else {
let callback = callbacks[id];
if (callback==null) throw Error(`Unknown callback ${id}`);
delete callbacks[id];
// genId should only be sent to the main thread when initializing the generator
if(data.genId) { data.result.genId = data.genId; }
if (data.error) callback[1](Error(data.error));
// genId should only be sent to the main thread when initializing the generator
else callback[0](data.result);
}
});
Expand Down