JavaScript Async - Basic Worker
Written by Ian Elliot   
Monday, 17 April 2023
Article Index
JavaScript Async - Basic Worker
The Trouble With Threads
Worker Thread To UI
The Event Problem

Worker Thread To UI Thread

Passing data from the Worker thread to the UI thread works in exactly the same way – only the other way round. You use the postMessage method of the DedicatedWorkerGlobalScope object in the Worker thread and attach an event handler for the message event in the UI thread. The DedicatedWorkerGlobalScope object can be accessed using this or self.

For example, in the Worker code:

this.postMessage({mydata:"some data"});

or

self.postMessage({mydata:"some data"});

notice that in this case you can use this or self to call postMessage because you are running inside the Web Worker.

This triggers a message event in the UI thread and you can define a handler and retrieve the data using:

worker.addEventListener("message", 
 function (e) {
   var somedata= e.data.mydata;
 });

Once again you have to be very clear that you understand what is running where. In this case the postMessage method is running on the Worker thread and the event handler is running on the UI thread.

This is about all there is to using Web Workers.

There are some details about error handling and terminating the thread before it is complete, but these are just details. The general idea is that you use the message event to communicate between the two threads.

There is one subtle point that is worth keeping in mind. The events that you trigger in passing data between the two threads will happen in the order that you trigger them, but they may not be handled promptly.

For example, if you start your Worker thread doing an intensive calculation then triggering a "how are you doing" message event from the UI thread might not work as you expect. It could be that the Worker thread is so occupied with its task that events are ignored until it reaches the end. The same happens with the messages passed from the Worker thread, but in this case the UI thread is generally not so focused on one task and so events usually get processed.

The rule is that UI thread events are generally handled promptly because that’s the way we tend to build UI code, but Worker thread events aren't because that’s the way we tend to build Worker code.

That is, events going from the Worker thread to the UI get processed as part of keeping the UI responsive. Events going the other way, i.e. from the UI thread to the Worker, are not so reliable. If you want to drive the Worker thread using events from the UI thread you basically have to design it to be event driven. This means writing the Worker thread as an event handler that responds to the messages that the UI thread sends.

A Simple Web Worker Example

As an example let's use the calculation of Pi example again.

First we need a new JavaScript file called pi.js containing the following code:

var state = {};
state.k = 0;
state.pi = 0;
var i;
for (i = 0; i < 10000000; i++) {
    state.k++;
    state.pi += 4 * Math.pow(-1, state.k + 1) / 
(2 * state.k - 1);
}
this.postMessage(state);

You can see that this worker is just a simple modification to the earlier program. The most important change is that we now no longer need to break the calculation up into chunks. As it is being performed on a separate thread it can run to completion and take as much time at it likes without any fear of blocking the UI – which is running on a different thread.

When the calculation is complete it uses the postMessage method to fire a "message" event on the UI thread and supply the result. Notice that we can pass as much data back to the UI thread as we like using an object.

The UI thread code is simply:

button1.addEventListener("click" ,
 function (event) {
  button1.setAttribute("disabled", true);
  var worker = new Worker("pi.js"); 
  worker.addEventListener("message",
   function (event) { 
result.innerHTML=event.data.pi; count.innerHTML=event.data.k; button.setAttribute("disabled", false); } ); });

Where we are using the UI of the previous examples. When the button is clicked the Worker constructor loads pi.js and starts it running on a new thread. The constructor returns a Worker object which runs on the UI thread in the variable worker.

When the Worker thread is finished it fires the "message" event which is handled by the anonymous function which displays the result.

This is a very typical use of Worker threads. The UI thread generally sends some data to initialize the Worker thread and then simply waits for message events. Notice that the button is disabled when the Worker thread has been started so as to avoid starting multiple Worker threads. There is nothing wrong with this if you actually want to start multiple threads, but it is easy to do so simply by accident unless you take steps to stop it happening.

If you try the program out you will find that on most modern hardware it is faster than the custom async version in the previous chapter. Using multiple threads is generally faster because it uses the hardware and any idle time more efficiently than using the event queue.



Last Updated ( Wednesday, 19 April 2023 )