如何将函数传递给 JavaScript Web Worker

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11909934/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 14:44:14  来源:igfitidea点击:

How to pass functions to JavaScript Web Worker

javascriptweb-worker

提问by Lachlan

I would like to pass a function (or functions) via a postMessage()to a web worker, because I can't refer to regular files.

我想通过 a 将一个函数(或多个函数)postMessage()传递给网络工作者,因为我无法引用常规文件。

To kick the web worker off, I am passing an object URL (created from a Blob) to the Workerconstructor. Then I am passing a message, but so far no luck putting a function in the message.

为了启动网络工作者,我将一个对象 URL(从 Blob 创建)传递给Worker构造函数。然后我传递了一条消息,但到目前为止还没有在消息中放置函数。

The (JSON) message cannot contain functions directly (as stipulated here), and although importScripts is theoretically allowed, I have not had any success using it so far in Chrome or Firefox.

在(JSON)消息不能直接包含的功能(如规定在这里),虽然importScripts理论上允许,我还没有使用它在Chrome或Firefox至今有过任何成功。

The body of the html file:

html文件的正文:

<div id="divText">1234</div>
<script>
    var greeter = function greet(name) {
        return "hello " + name;
    };
    function webWorkerWorker() {
        self.postMessage("started1");
        self.onmessage = function(event) {
            importScripts(event.data.content);
            self.postMessage("importScripts success");
            var result = greeter("john");
            self.postMessage(result);
        };
    }
    var functionBody = mylib.extractFunctionBody(webWorkerWorker);
    var functionBlob = mylib.createBlob([functionBody]);
    var functionUrl = mylib.createObjectURL(functionBlob);

    var functionBody2 = mylib.extractFunctionBody(greeter);
    var functionBlob2 = mylib.createBlob([greeter]);
    var functionUrl2 = mylib.createObjectURL(functionBlob2);

    var worker = new Worker(functionUrl);
    worker.onmessage = function(event) {
        document.getElementById("divText").innerHTML = event.data;
    }
    worker.postMessage({
                type: "init",
                content: functionUrl2
            });
</script>

Currently it results in setting the divText value to "importScripts success".

目前它导致将 divText 值设置为“importScripts success”。

Am I doing something wrong? Is there another way that functions can be passed to web workers? Or is it not possible?

难道我做错了什么?还有另一种方法可以将函数传递给网络工作者吗?或者不可能?

采纳答案by Lachlan

Turns out this method works fine, there was merely a bug in my worker:

事实证明这种方法工作正常,我的工作人员只有一个错误:

var result = greeter("john");

should be

应该

var result = greet("john");

which makes sense - I'm passing the greeter variable to the worker, but there's no reason for it to know the variable name of the object I'm passing.

这是有道理的 - 我正在将 greeter 变量传递给工作人员,但没有理由知道我正在传递的对象的变量名称。

回答by vadimk

For those who are looking for more generic answer: here is a plugin, which allows you to execute any function of your javascript code in a thread.

对于那些正在寻找更通用答案的人:这是一个插件,它允许您在线程中执行 javascript 代码的任何功能。

http://www.eslinstructor.net/vkthread/

http://www.eslinstructor.net/vkthread/

Consider it as "function outsourcing". You pass any function to the plugin as an argument and get result in callback. You also can "outsource" object's methods, function with dependecies, anonymous function and lambda.

将其视为“功能外包”。您将任何函数作为参数传递给插件并在回调中获得结果。您还可以“外包”对象的方法、具有依赖关系的函数、匿名函数和 lambda。

Enjoy.

享受。

--Vadim

--瓦迪姆

回答by Fernando Carvajal

Yes, of course it is possible, I implemented it

是的,当然有可能,我实现了

This is a promise that will execute the generic worker

这是一个将执行通用工作者的承诺

/*
    @data.context, The context where the callback functions arguments are, ex: window
    @data.callback, ["fn_name1", "fn_name2", function (fn1, fn2) {}]
        The callback will be executed, and you can pass other functions to that cb
    @worker_url string url of generic web worker
*/
function genericWorker(worker_url, data) {
    return new Promise(function (resolve, reject) {

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

        var callback = data.callback.pop()
        var functions = data.callback
        var context = data.context

        if (!worker_url)
            return reject("Worker_url is undefined")

        if (!callback)
            return reject("A callback was expected")

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

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

        var worker = new Worker(worker_url)

        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("}"))
            }
        }

    })
}

The generic worker file worker_for_anything.js

通用工作文件 worker_for_anything.js

self.addEventListener('message', function(e) {    
    var worker_functions = {} //Functions used by callback
    var args = [] //Arguments of the callback

    for (fn of e.data.functions) {
        worker_functions[fn.name] = new Function(fn.args, fn.body)
        args.push(fn.name)
    }

    var callback = new Function( e.data.callback.args, e.data.callback.body) //Callback passed and ready to be executed    
    args = args.map((fn_name) => { return worker_functions[fn_name] }) //FUnctions loaded as arguments
    var result = callback.apply(null, args) //executing callback with function arguments
    self.postMessage( result )

}, false)

Using it :)

使用它 :)

var data = {
    context: window, //the context of the functions passed, ex: window for blockCpu
    callback: ["blockCpu", function (bla) {
        bla(7000) //blocking for 7000 ms
        return "bla" //This return is catched in the promise
    }]
}

genericWorker("/worker_for_anything.js", data)
    .then(function (result){
        console.log("result", result)

    }).catch((error)=> { console.log(error) })

//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;
    }   
}