You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
@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.
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 export function foo(){}). It actually turns that code into something that looks like CommonJS before running it in the Worker, which is important for reasons I'll get to in a minute.
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 export things from a function, and without being exported Workerize can't find your nested functions and make them callable from the main thread:
const{ a, b }=workerize(function(){exportfunctiona(){return1}exportfunctionb(){return2}// ^^ these are Syntax Errors :(})
Solution: use CommonJS
So 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 exports object, which you can decorate with named functions you want to make callable from the main thread!
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 expose(), which takes a function name and defines it as method on the worker.
Obviously 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!
constworker=workerize(function(exports){/* export function a() {} export function b() {} */exports.a=()=>1;exports.b=()=>2;});worker.a().then(console.log)// 1
One 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:
None 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...functioncomlinker(func){returnComlink.wrap(newWorker(URL.createObjectURL(newBlob(['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:constworker=comlinker(exports=>{exports.foo=async()=>'bar';});worker.foo().then(console.log);// 'bar'
Alternative 2: Workerize implemented using Comlink
It'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:
Activity
ChristianMurphy commentedon Apr 4, 2019
https://github.com/GoogleChromeLabs/comlink#comlinkproxyvaluevalue could be one option
chase-moskal commentedon Apr 29, 2019
huh? i don't get it either
@ChristianMurphy, the example you linked to shows:
const api = Comlink.proxy(new Worker("worker.js"));however
"worker.js"is a string — not a function..does comlink have anything to do with workerize?
bestwestern commentedon May 27, 2019
@ChristianMurphy thank you.
@chase-moskal No - I think Christian proposed I used another library.
@developit is brilliant but he should get an intern to do documentation for mere mortals ;)
kairos666 commentedon Oct 5, 2019
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.
dandv commentedon Oct 19, 2019
@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?
developit commentedon Dec 28, 2019
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
export function foo(){}). It actually turns that code into something that looks like CommonJS before running it in the Worker, which is important for reasons I'll get to in a minute.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
exportthings from a function, and without being exported Workerize can't find your nested functions and make them callable from the main thread:Solution: use CommonJS
So 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
exportsobject, which you can decorate with named functions you want to make callable from the main thread!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
expose(), which takes a function name and defines it as method on the worker.Automate exports using a comment annotation
Obviously 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!
I've created a demo of this code on JSFiddle.
Automate exports by parsing with a Regex
One 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:
(demo)
Alternative: Use Comlink
None 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:
Alternative 2: Workerize implemented using Comlink
It'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:
https://jsfiddle.net/developit/kr5j1d6v/