3

It seems the only communication from host to worker is postMessgage and onmessage. If the worker requires some dynamic initialization (as in constructor, here: regular expression to use later), what is the best way to do this?

A possibility would be to let data be an object, and have e.g. an action parameter, and to check this on every run. This seems a bit kludgey.

serv-inc
  • 35,772
  • 9
  • 166
  • 188
  • What's the question exactly? How to pass non string data to the Worker? Yes`postMessage` will happily clone most js objects and even transfer some, so it's indeed the correct place. If you were asking for something else, could you please show a more concrete example? – Kaiido Oct 26 '19 at 14:58
  • 1
    @Kaiido: it seems kludgey and inefficient to have an if/else in each `postMessage`, if the if-branch is used only once during initialization, and the else branch is run every other time. So the question is if workers provide for some kind of initialization data/constructor-like functionality. – serv-inc Oct 26 '19 at 15:41
  • @Kaiido: maybe similar to Akka's props: https://doc.akka.io/docs/akka/2.1/java/untyped-actors.html – serv-inc Oct 26 '19 at 15:58
  • So you mean you do setup a full Worker for a single computation? That is inneficient. Starting a worker context is far more work for the browser than a simple if else. Make it do more things, the initial "define which operation to perform" step will look less superfluous to your eyes. – Kaiido Oct 27 '19 at 00:06
  • @Kaiido: The worker should be called multiple times. Yet, if there is only one interface, there needs to be some kind of branching to switch between "computation" and "initialization" mode. This if/else seems inefficient. – serv-inc Oct 27 '19 at 10:46
  • I really don't understand your concerns. You get the fact that to start a whole new js context is a far bigger operation than a simple if/else branching right? Is your worker really only doing the same computation every time so that it can be "initialized" only once? Won't your regex change during your page's lifetime? If so, just pass this regex along with the data you wish to process, if we use web-workers it's to make heavy computations on an other thread than the one where the UI is, so it's not blocked. A little more computation on that side is not a concern. – Kaiido Oct 27 '19 at 12:38
  • 1
    @Kaiido: many other languages with actors/workers have some kind of initialization. Using message passing for this makes code less clear to read, as it moves the initialization inside the message passing. This is the opposite of separation of concerns. As said before, akka has Worker initialization. As of https://en.wikipedia.org/wiki/Erlang_(programming_language), it seems that Erlang has some initialization as well. It is understandable if JS workers do not have this, being relatively young. Still, it is a feature of parallel programming that a) makes sense and b) is used by the big players. – serv-inc Oct 29 '19 at 05:31

2 Answers2

1

The communication channel between different agents is very low-level, but you can easily build a higher level communication based on that. I'd use objects to communicate different "events":

  { event: "init", data: [/d/] }

Based on these events, you can create different events to represent e.g. function calls and their response:

 { event: "call-init", data: [/d/] } // >>>
 { event: "return-init", data: ["done"] } // <<<

Then you can build a wrapper around that, that sends and handles the response, something like:

    async function call(name, ...args) {
      channel.send("call-" + name, args);
      return await cannel.once("return-" + name);
   }

   channel.init = call.bind(null, "init");

Then your code turns into something along the lines of:

  const channel = new Channel(worker);
  await channel.init(/d/);
 await channel.doOtherStuff();

That's just to give you the basic idea.

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • 1
    You can find a basic implementation of this concept [here](https://github.com/Jonasdoubleyou/uni-def/blob/master/IDE/src/interpreter/channel.ts). I bet there are whole libraries out there. – Jonas Wilms Oct 26 '19 at 13:27
  • 2
    Not sure to get everything neither in the question nor in the answer... but do you know the [MessageChannel API](https://developer.mozilla.org/en-US/docs/Web/API/MessageChannel/MessageChannel)? If you wish to build a `once` promise based communication channel, that's probably the best way, since you can both continue to send other messages in parallel to the Worker and be sure you'll only receive only one response to this MessageChannel (if you built your communication this way). See [this answer](https://stackoverflow.com/questions/55498605/55549411#55549411) for a very basic implementation. – Kaiido Oct 26 '19 at 14:55
1

A more ad-hoc approach than Jonas' solution abuses the Worker's name option: You can e.g. pass the regex string in this name and use it later:

test.js

var a = new Worker("worker.js", {name: "hello|world"})

a.onmessage = (x) => {
  console.log("worker sent: ")
  console.log(x)
}
 
a.postMessage("hello")

worker.js

var regex = RegExp(this.name);

onmessage = (a) => {
  if (regex.test(a.data)) {
    postMessage("matches");
  } else {
    postMessage("no match for " + regex);
  }
}
serv-inc
  • 35,772
  • 9
  • 166
  • 188