Offloading work to a dedicated worker

Workers give us the ability to offload long-running, computationally-intensive tasks to the background. Instead of having to make sure our event loop is not filled with some type of heavy task, we can offload that task to a background thread.

In other languages/environments, this might look like the following (this is only pseudo-code and is not really tied to any language):

Thread::runAsync((data) -> {
for(d : data) { //do some computation }
});

While this works well in those environments, we have to start thinking about topics such as deadlock, zombie threads, read after write, and so on. All of these can be quite hard to comprehend and are usually some of the most difficult bugs that can be encountered. Instead of JavaScript giving us the capability of utilizing something like the preceding, they gave us workers, which give us another context to work in where we don't face the same issues.

For those that are interested, a book on operating systems or Unix programming can help shed light on the preceding issues. These topics are out of the scope of this book, but they are quite interesting and there are even languages that are trying to address these issues by building the workarounds into the languages. Some examples of these are Go (https://golang.org/), which uses a technique of message passing, and Rust (https://www.rust-lang.org/), which utilizes the concept of borrow checking and such to minimize these issues.

To start off with an example of work being done in the background, we are going to spawn a Worker and have it compute a sum over 1 million numbers. To do this:

  1. We add the following script section to our HTML file:
<script type="text/javascript">
const worker = new Worker('worker.js');
console.log('this is on the main thread');
</script>
  1. We create a JavaScript file for our Worker and add the following:
let num = 0;
for(let i = 0; i < 1000000; i++) {
num += i;
}

If we launch Chrome, we should see two messages printed – one that says it was run on the main thread and another with the value 499999500000. We should also see that one was logged by the HTML file and the other was logged by the worker. We have just spawned a worker and got it to do some work for us!

Remember that if we want to run JavaScript files from our filesystem and not a server, we will need to close out of all instances of Chrome and then relaunch it from the command line using chrome.exe –-allow-file-access-from-files. This will give us access to launch our external JavaScript files from the filesystem and not need a server.

Let's go ahead and do something a little more complex that the user may want to do. One interesting math problem is getting the prime factorization for a number. This means that, when given a number, we will try to find all of the prime numbers (numbers that are only divisible by one and itself) that make up that number. An example would be the prime factorization of 12, which is 2, 2, and 3.

This problem leads to the interesting field of cryptography and how public/private keys work. The basic understanding is that, given two relatively large prime numbers, multiplying them is easy, but finding those two numbers from that product of them is infeasible due to time constraints

Back to the task at hand, what we will do is spawn a worker after the user inputs a number into an input box. We will compute that number and log it to the console. So let's begin:

  1. We add an input to our HTML file and change the code to spawn a worker on the change event for that input box:
<input id="in" type="number" />
<script type="text/javascript">
document.querySelector("#in").addEventListener('change', (ev) => {
const worker = new Worker('worker.js', {name :
ev.target.value});
});
</script>
  1. Next, we will grab our name in the worker and use that as the input. From there, we will run the prime factorization algorithm found at https://www.geeksforgeeks.org/print-all-prime-factors-of-a-given-number/, but transposed to JavaScript. Once we are done, we will turn off the worker:
let numForPrimes = parseInt(self.name);
const primes = [];
console.log('we are looking for the prime factorization of: ', numForPrimes);
while( numForPrimes % 2 === 0 ) {
primes.push(2);
numForPrimes /= 2;
}
for(let i = 3; i <= Math.sqrt(numForPrimes); i+=2) {
while( numForPrimes % i === 0 ) {
primes.push(i);
numForPrimes /= i;
}
}
if( numForPrimes > 2 ) {
primes.push(numForPrimes);
}
console.log('prime factorization is: ', primes.join(" "));
self.close();

If we now run this application in the browser, we will see that after each input we get the console log message in the console. Notice that there is no factor for the number 1. There is a mathematical reason for this, but just note that there is no prime factorization for the number 1.

We can run this for a bunch of inputs, but if we put in a relatively large number such as 123,456,789, it will still compute it in the background as we do things on the main thread. Now, we are currently passing data to the worker through the name of the worker. There has to be a way to pass data between the worker and the main thread. This is where the postMessage and BroadcastChannel APIs come into play!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.136.154.103