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
How to pass a function #37
Comments
https://github.com/GoogleChromeLabs/comlink#comlinkproxyvaluevalue could be one option |
huh? i don't get it either @ChristianMurphy, the example you linked to shows: however ..does comlink have anything to do with workerize? |
@ChristianMurphy thank you. @developit is brilliant but he should get an intern to do documentation for mere mortals ;) |
I've had the same problem and couldn't figure out how to directly use a function as parameter. Found a workaround though that may help you keep your code DRY. function foo() { return 3; }
function bar(multiply) { return foo() * multiply; }
const inlineWorkerFile = `
${ foo.toString() }
export ${ bar.toString() }
`
let worker = workerize(inlineWorkerFile);
(async () => {
console.log('3 * 3 = ', await worker.bar(3));
console.log('3 * 1 = ', await worker.bar(1));
})(); |
@kairos666: the code seems to be doing pretty much what you're suggesting but when trying to actually pass a function, I see that the arguments are not passed to the created worker. @surma, since you write a compelling advocacy post on using Workers, might you have a look at this? |
This turned into documentation, my apologies.Passing a function instead of a string is a little more involved than running that function in a Worker. Workerize functions by parsing the code for the string (or function) you pass to find ES Modules exports (like Broken: "Function exports" (not a thing)Unfortunately, there's no way to use exports within a Function - the export syntax only works at the root of a module and can't be inside of blocks or functions. So, while Workerize supports passing a function, it's not possible to const { a, b } = workerize(function() {
export function a() { return 1 }
export function b() { return 2 }
// ^^ these are Syntax Errors :(
}) Solution: use CommonJSSo what can we do? As I mentioned above, Workerize transforms ES Modules exports into roughly CommonJS before running code in the Worker context. When passing a function, the first argument to that function is an There's one hiccup though - because we're skipping the ES Modules parsing step, Workerize needs to be told what function names we have available rather than inferring them from our code. For this, there's a method on all Workerize instances called const worker = workerize(function(exports) {
exports.a = () => 1;
exports.b = () => 2;
});
worker.expose('a');
worker.expose('b');
worker.a().then(console.log) // 1 Automate exports using a comment annotationObviously this is a little bit more manual than we'd like. However, there's a solution! Workerize's parsing of exports is extremely naive and can be easily tricked. It doesn't even know about comments. That means we can annotate our methods using a comment and have Workerize detect those exports! const worker = workerize(function(exports) {
/*
export function a() {}
export function b() {}
*/
exports.a = () => 1;
exports.b = () => 2;
});
worker.a().then(console.log) // 1 I've created a demo of this code on JSFiddle. Automate exports by parsing with a RegexOne risk with the comment decoration approach is that most production minifier setups remove comments. That's definitely not good for us, since doing so would remove the "mirrored" versions of our exported functions on the main thread. Here's a (very) naive wrapper around Workerize that finds commonjs-style exports and exposes them on the Workerize instance: function workerizeCjs(func) {
const w = workerize(func);
String(func).match(/\bexports\.[\w$]+/g).map(f=>w.expose(f.substr(8)));
return w;
} (demo) Alternative: Use ComlinkNone of these are amazing solutions TBH. For runtime usage, Comlink has far more to offer here and isn't that much larger (it's around 1.1kb). Here's a version of Workerize that is built with Comlink: // Assuming Comlink is in scope on the main thread...
function comlinker(func) {
return Comlink.wrap(new Worker(URL.createObjectURL(new Blob([
'importScripts("https://unpkg.com/comlink@4.2.0/dist/umd/comlink.min.js");var exports={},module={exports:exports};('+func+')(exports);Comlink.expose(module.exports);'
]))));
}
// same as workerize, but with all of Comlink's nice nesting+callback+transferables support:
const worker = comlinker(exports => {
exports.foo = async () => 'bar';
});
worker.foo().then(console.log); // 'bar' Alternative 2: Workerize implemented using ComlinkIt's a little awkward to have to load the Comlink dependency separately on the worker and main thread, so I created a demo showing a pared-down inline version of Comlink that shares itself with the Worker thread. That sharing bit is Workerize's main selling point, so you could consider this to be a version of Workerize implemented using Comlink: |
Could you please show in the example how to pass a function?
The text was updated successfully, but these errors were encountered: