3

How is it possible to send a (copy of) function from my main thread to my worker?

Whenever I try: worker.postMessage({data, func: (data) => data), firefox gives me an error message: DataCloneError: The object could not be cloned.

As for Chrome, the message differ, but the error still occur: Uncaught DOMException: Failed to execute 'postMessage' on 'Worker': An object could not be cloned.

Wanderson Silva
  • 1,179
  • 1
  • 17
  • 35
  • Does this answer your question? [How to pass functions to JavaScript Web Worker](https://stackoverflow.com/questions/11909934/how-to-pass-functions-to-javascript-web-worker) – Dane Brouwer Jul 22 '20 at 09:40

2 Answers2

1

Yes, it is possible, I did that, and better than that I can create web workers without js files, just using Blob files generated with javascript code.

setInterval(()=>{console.log("non bloked " + Math.random())}, 900)

console.log("starting blocking code in Worker")
console.time("blocked")

genericWorker(window, ["blockCpu", function (block){    
    block(10000) //This blockCpu function is defined below
    return `\n\nbla ${123123*2312} bla \n` //This is catched in the resolved promise

}]).then(function (result){
    console.timeEnd("blocked")
    console.log("End of blocking code", result)
})
.catch(function(error) { console.log(error) })


/*  A Web Worker that does not use a File, it create that from a Blob
    @cb_context, The context where the callback functions arguments are, ex: window
    @cb, ["fn_name1", "fn_name2", function (fn1, fn2) {}]
        The callback will be executed, and you can pass other functions to that cb
*/
function genericWorker(cb_context, cb) {
    return new Promise(function (resolve, reject) {

        if (!cb || !Array.isArray(cb))
            return reject("Invalid data")

        var callback = cb.pop()
        var functions = cb

        if (typeof callback != "function" || functions.some((fn)=>{return typeof cb_context[fn] != "function"}))
            return reject(`The callback or some of the parameters: (${functions.toString()}) are not functions`)

        if (functions.length>0 && !cb_context)
            return reject("context is undefined")

        callback = fn_string(callback) //Callback to be executed
        functions = functions.map((fn_name)=> { return fn_string( cb_context[fn_name] ) })

        var worker_file = window.URL.createObjectURL( new Blob(["self.addEventListener('message', function(e) { var bb = {}; var args = []; for (fn of e.data.functions) { bb[fn.name] = new Function(fn.args, fn.body); args.push(fn.name)}; var callback = new Function( e.data.callback.args, e.data.callback.body); args = args.map(function(fn_name) { return bb[fn_name] });  var result = callback.apply(null, args) ;self.postMessage( result );}, false)"]) )
        var worker = new Worker(worker_file)

        worker.postMessage({ callback: callback, functions: functions })

        worker.addEventListener('error', function(error){ return reject(error.message) })

        worker.addEventListener('message', function(e) {
            resolve(e.data), worker.terminate()
        }, false)

        //From function to string, with its name, arguments and its body
        function fn_string (fn) {
            var name = fn.name, fn = fn.toString()

            return { name: name, 
                args: fn.substring(fn.indexOf("(") + 1, fn.indexOf(")")),
                body: fn.substring(fn.indexOf("{") + 1, fn.lastIndexOf("}"))
            }
        }
    })
}

//random blocking function
function blockCpu(ms) {
    var now = new Date().getTime();
    var result = 0
    while(true) {
        result += Math.random() * Math.random();
        if (new Date().getTime() > now +ms)
            return;
    }   
}
Fernando Carvajal
  • 1,869
  • 20
  • 19
  • You've posted almost the exact same message [here](https://stackoverflow.com/a/47804656/2418790). However your implementation differs - would you update either of your messages to your preferred implementation. This question should of been flagged as a duplicate. – Dane Brouwer Jul 22 '20 at 09:41
0

Not tested this yet, but it seems that from the spec, only objects that can be send to a Worker are "Transferable" objects: https://developer.mozilla.org/en-US/docs/Web/API/Transferable

I found some examples, in the first one the process is not detailled, but i'm guessing he uses a kind or URL encode, for an object, it can be converted to JSON like in the second example.

How to pass functions to JavaScript Web Worker

Passing objects to a web worker

I have to do this too in a couple of days, i can keep updated with what i have that will be tested..

Community
  • 1
  • 1
Kaddath
  • 5,933
  • 1
  • 9
  • 23
  • To be able to post any function, anonymous included, I needed to call [toString](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Function/toString) on it and then [eval](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#eval_as_a_string_defining_function_requires_(_and_)_as_prefix_and_suffix) it from inside the worker. – Wanderson Silva Feb 17 '17 at 14:04