Skip to content Skip to sidebar Skip to footer

How To Let A Webworker Do Multiple Tasks Simultaneously?

I am trying to let a Web-Worker manage its state, meanwhile serving multiple async requests. worker.ts file let a =0; //this is my worker's state let worker=self as unknown as Wor

Solution 1:

Your problem is that you are trying to take an event based API and use it as a Promise based one, but events may fire multiple times, while Promise should resolve only once.

The communication between the Worker and the main thread works by sending and receiving messages, but there is by default no one-to-one relation between these messages. Both ends of the communication (ports) will simply stack incoming messages, and handle them sequentially, when they'll get time.

In your code, the main thread's worker.onmessage handler of f1 has been overwritten by the second call f2 synchronously (one microtask later, but that's still synchronous for our matter). You could attach your event using the addEventListener method, at least this way it wouldn't be overwritten. But even then, when the first message event will fire on worker, both handlers will think it's there own message that did arrive, while in fact it was the one of f2. so that's not what you need...

What you need is to set up a protocol of communication which would allow both ends to identify each task. You could for instance wrap all your tasks' data with an object containing a .UIID member, be sure both ends wraps their message this way, and then from main thread check that UUID to resolve the appropriate Promise.

But that can become a bit complicated to implement and to use.


My personal favorite way is to create a new MessageChannel per task. If you don't know this API, I invite you to read this answer of mine explaining the basics.

Since we are sure the only one message that will come through this MessageChannel is the response from the Worker to the one task we sent to it, we can await it just like a Promise.

All we have to do, is to make sure that in the Worker thread we respond through the transferred port instead of the global scope.

const url = getWorkerURL();
const worker = newWorker(url)

constworkerFunc = (op) => {
  // we create a new MessageChannelconst channel = newMessageChannel();
  // we transfer one of its ports to the Worker thread
  worker.postMessage(op, [channel.port1]);

  returnnewPromise((res,rej) => {
    // we listen for a message from the remaining port of our MessageChannel
    channel.port2.onmessage = (evt) =>res(evt.data);
  });
}

(async () => {
  const f1 = awaitworkerFunc("+1");
  console.log("f1", f1);
})();

(async () => {
  const f2 = awaitworkerFunc("+2");
  console.log("f2", f2);
})()


// SO onlyfunctiongetWorkerURL() {
  const elem = document.querySelector( '[type="worker-script"]' );
  const script = elem.textContent;
  const blob = newBlob( [script], { type: "text/javascript" } );
  returnURL.createObjectURL( blob );
}
<scripttype="worker-script">let a = 0;
const worker = self;

worker.onmessage = (evt) => {
  const port = evt.ports[0]; // this is where we will respondif (evt.data === "+1") {
    setTimeout(() => {
      a = a + 1;
      // we respond through the 'port'
      port.postMessage(a);
    }, 3000);
  }
  elseif (evt.data === "+2") {
    setTimeout(() => {
      a = a + 2;
      // we respond through the 'port'
      port.postMessage(a);
    }, 1000)
  }
};
</script>

Post a Comment for "How To Let A Webworker Do Multiple Tasks Simultaneously?"