Как передать функции JavaScript веб-работнику
Я хотел бы передать функцию (или функции) через postMessage() веб-рабочему, потому что я не могу ссылаться на обычные файлы.
Чтобы отключить веб-исполнителя, я передаю URL-адрес объекта (созданный из Blob) в конструктор Worker. Затем я передаю сообщение, но до сих пор не удавалось помещать функцию в сообщение. Сообщение (JSON) не может содержать функции напрямую (как указано здесь), и хотя importScripts теоретически разрешены, я не имел никакого успеха, используя его пока что в Chrome или Firefox.
Тело 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>
В настоящее время это приводит к установке значения divText в значение "importScripts success".
Я что-то делаю неправильно? Есть ли другой способ, которым функции могут передаваться веб-работникам? Или это невозможно?
Ответы
Ответ 1
Оказывается, этот метод работает отлично, в моем работнике была только ошибка:
var result = greeter("john");
должен быть
var result = greet("john");
что имеет смысл - я передаю переменную greeter для рабочего, но нет никакой причины для этого знать имя переменной объекта, который я передаю.
Ответ 2
Для тех, кто ищет более общий ответ:
вот плагин, который позволяет вам выполнять любую функцию вашего javascript-кода в потоке.
http://www.eslinstructor.net/vkthread/
Рассмотрим это как "аутсорсинг функций". Вы передаете любую функцию плагину в качестве аргумента и получаете результат в обратном вызове. Вы также можете "аутсорсинг" методов объектов, функции с помощью dependecies, анонимной функции и лямбда.
Enjoy.
- Вадим
Ответ 3
Да, конечно, возможно, я реализовал его
Это обещание, которое выполнит общий рабочий
/*
@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("}"))
}
}
})
}
Общий рабочий файл 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)
Используя его:)
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;
}
}