Reputation: 2516
Reading about web workers I stumbled across this example from mdn. The worker.js
is just once simple function.
So when we post a message to the worker the onmessage
part is then started by the worker. In the simple example the worker just multiplies two numbers. But what if I want a worker that can also add, divide etc? Do I need to create a new worker file (e.g. worker_add.js
) for each function I want my worker to run? Or what is the cleaner way to deal with this?
I was thinking of having a string posted as first argument to the worker
myWorker.postMessage(["method", arg1, arg2]);
and then in my worker I have if/else
conditionals which check if the string matches and then execute different code.
importScripts('emscripten.js')
onmessage = function(e) {
console.log('Message received from main script.');
if (e.data[0] == "method1")
{
Module.method1(e.data[1].byteOffset, e.data[2]);
postMessage(e.data[1]);
}
else if (e.data[0] == "method2")
{
var ret= Module.method2();
postMessage(ret);
}
console.log('Posting message back to main script');
}
Upvotes: 2
Views: 479
Reputation: 2516
In the spirit of Emil's answer I found an example on html5rocks. I feel like this is slightly cleaner than Emil's answer.
The main script looks like this
<button onclick="sayHI()">Say HI</button>
<button onclick="unknownCmd()">Send unknown command</button>
<button onclick="stop()">Stop worker</button>
<output id="result"></output>
<script>
function sayHI() {
worker.postMessage({'cmd': 'start', 'msg': 'Hi'});
}
function stop() {
// worker.terminate() from this script would also stop the worker.
worker.postMessage({'cmd': 'stop', 'msg': 'Bye'});
}
function unknownCmd() {
worker.postMessage({'cmd': 'foobard', 'msg': '???'});
}
var worker = new Worker('doWork2.js');
worker.addEventListener('message', function(e) {
document.getElementById('result').textContent = e.data;
}, false);
</script>
and the worker doWork2.js
like this:
self.addEventListener('message', function(e) {
var data = e.data;
switch (data.cmd) {
case 'start':
self.postMessage('WORKER STARTED: ' + data.msg);
break;
case 'stop':
self.postMessage('WORKER STOPPED: ' + data.msg +
'. (buttons will no longer work)');
self.close(); // Terminates the worker.
break;
default:
self.postMessage('Unknown command: ' + data.msg);
};
}, false);
Upvotes: 1
Reputation: 6366
Since you can pass stringified AJAX, you can easily implement conditional logic in your worker.
Simply pass an argument to tell the worker what to do:
//This snippet won't run, it's just to illustrate
//main.js
var worker = new Worker('/js/worker.js');
worker.addEventListener("message", function(event) {
if (event.data["do"] == "main") {
console.log("One task complete");
} else if (event.data["do"] == "second") {
console.log("Other task complete");
}
}, false);
function doMainTask(taskParam) {
worker
.postMessage({
taskParam: JSON.stringify(taskParam),
"do": "main"
});
}
function doSecondTask(taskParam) {
worker
.postMessage({
taskParam: JSON.stringify(taskParam),
"do": "second"
});
}
//worker.js
function messageHandler(event) {
var obj = JSON.parse(event.data);
var str = "";
if (obj["do"] == "main") {
str = main(obj.taskParam);
} else if (obj["do"] == "second") {
str = second(obj.taskParam);
}
this.postMessage({
msg: str,
"do": obj["do"],
obj: obj
});
}
this.addEventListener('message', messageHandler, false);
function main(taskParam) {
return "main";
}
function second(taskParam) {
return "second";
}
Upvotes: 2
Reputation: 24541
I don't think there is a better way of doing the things you want but passing some metadata within the message itself.
However your implementation could be easily improved. Instead of switcher, just always treat the first argument as a function name, all others are arguments to this function and the function result is directly posted back:
onmessage = function(e) {
postMessage(Module[e.data[0]].apply(Module, e.data.slice(1)));
}
Simple one line which cares about any amount of the functions in your worker.
If worker function result is asynchronous you can check that the function result is e.g. Promise
and post the message on promise resolve
instead of just posting the message directly.
Upvotes: 2