0

I need to have a JavaScript web worker that maintains its own internal state (not just replies to messages). It also needs to do be able to do a computation until it is told to stop (by a message being sent). Something like the following:

// worker.js
initialState = () => {...}
updateState = (state) => {...}
updateStateWithMessage = (state, message) => {...}

state = initalState()
state = updateStateWithMessage(state, self.getmessage())
while (true) {
    while (!self.hasmessage()) {
        state = updateState(state)
    }
    state = updateStateWithMessage(state, self.getmessage())
    self.postmessage(state)
}

//main.js
worker = new Worker("worker.js")
worker.onmessage = (event) => console.log(event.data)
onClick() {
    worker.postMessage("Here is some data.")
}

I couldn't think of a way to implement this with a single self.onmessage callback in the worker (which is how I have seen most examples of Web Workers operating) since it replies on maintaining its own internal state. So I invented the fictitious self.getmessage and self.hasmessage functions. Can anyone show me how to actually implement this or something similar.

Thanks in advance!

Amaar
  • 337
  • 2
  • 13

1 Answers1

0

I'm not really sure if I understand your question.

If you want your web worker to be stateful and perform different tasks according to the message the main thread sent to the worker, you will need to implement your own messaging system. You will need to establish some kind of convention for your messages, and use some way to distinguish between the incoming messages, both in the main thread and in your web worker. Basically you need to implement a very lightweight version of Redux.

For example, I wrote a Three.js app where I render a bitmap in an OffscreenCanvas in a web worker.

First, I established a convention for the messages exchanged between main thread and worker. All my messages have a string representing the action the message is all about, a payload containing some optional data, and a source stating who sent the message (i.e. the main thread or the web worker).

In my app, the main thread can send 2 types of messages to the worker, while the worker can sen 3 types of messages.

// Actions sent by main thread to the web worker
export const MainThreadAction = Object.freeze({
  INIT_WORKER_STATE: "initialize-worker-state",
  REQUEST_BITMAPS: "request-bitmaps"
});

// Actions sent by the web worker to the main thread
export const WorkerAction = Object.freeze({
  BITMAPS: "bitmaps",
  NOTIFY: "notify",
  TERMINATE_ME: "terminate-me",
});

My web worker is stateful, so in my worker.js file I have this code:

// internal state of this web worker
const state = {};

The worker can discern the messages sent by the main thread with a simple switch statement, handle the message, and respond to the main thread with postMessage:

// worker.js
const onMessage = event => {
  const label = `[${event.data.source} --> ${NAME}] - ${event.data.action}`;
  console.log(`%c${label}`, style);

  switch (event.data.action) {
    case MainThreadAction.INIT_WORKER_STATE:
      // handle action and postMessage
      break;
    case MainThreadAction.REQUEST_BITMAPS: {
      // handle action and postMessage
      break;
    }
    default: {
      console.warn(`${NAME} received a message that does not handle`, event);
    }
  }
};
onmessage = onMessage;

The code running in the main thread also uses a switch statement to distinguish between the messages sent by the worker:

// main.js
switch (event.data.action) {
  case WorkerAction.BITMAPS: {
    // handle action
    break;
  }
  case WorkerAction.TERMINATE_ME: {
    worker.terminate();
    console.warn(`${NAME} terminated ${event.data.source}`);
    break;
  }
  case WorkerAction.NOTIFY: {
    // handle action
    break;
  }
  default: {
    console.warn(`${NAME} received a message that does not handle`, event);
  }
}
jackdbd
  • 4,583
  • 3
  • 26
  • 36