Web Workers in JavaScripts.

Web Workers in JavaScripts.

How can we achieve concurrency in Javascript?

Hey Guys, In this article I will be talking about one of the most interesting features of JavaScript i.e Web Workers. Javascript has many bottlenecks like performance, browser compatibility, and accessibility. Just like this one of the drawbacks is its single-threaded nature, JavaScript is unable to perform multiple tasks at a single time. For Example let's say you are building a large-scale web application that involves continuous UI Events, Query and handle a huge amount of API data and handling DOM.

Unfortunately, we can not handle all of this simultaneously because script execution happens in a single thread environment. We do try to replicate concurrency using setTimeout(), setInterval(), XMLHttpRequest, and event handlers. They do provide asynchronous nature but it is not the same thing. Asynchronous events are processed after the currently executing script has yielded.

Here comes the concept called Web Workers, which provides you the way to execute multiple scripts concurrently, handle huge API data, and do some computationally intensive tasks without blocking the UI or other scripts that handle UI. There are several types of Web Workers:-

  • Dedicated Web Workers - This one is used as my only single script.
  • Shared Web Workers - They are shared among multiple scripts across the windows or Iframes, only if the web worker and scripts domain are the same.
  • Service Workers - These are special kinds of web Workers, they act as a proxy between browser(client) and network. They can incept the outgoing requests, they allow us to use Push(Push Notification) and Background Sync API.

In this article we will be talking about Dedicated Web Workers, soon in upcoming articles I will be covering Shared and Service Worker API.

Web worker does not have the access to the global context(global scope object), the window object is available but not directly accessible, they are access through the shared mixin WindowOrWorkerGlobalScope, with the help of this mixin, they have their own WorkerGlobalScope(derived). In simple terms, you can not access javascript globals in web Workers you must use WorkerGlobalScope, some of the common accessible API's are, atob(), btoa(), clearInterval(), clearTimeout(),dump() , setInterval(), setTimeout(), Here is the list of all the available API.

Yeah! As we know Web Workers works in an isolated thread, that is why they needed to write in a separate file, before doing any code we need to create a Worker Object on our main page, like this

let worker = new Worker('web-worker.js');

The web-worker.js is your worker file(path to the file. here it is in the root directory), the worker will not start working until your given file is completely downloaded and executed, if your file does not exist or fail to load, your worker will fail silently(Sad but True😥). The Communication between Worker and parent page is done via, Event Model and postMessage() method, postMessage() accepts a single parameter a JSON Object or a String.

Let me give you a simple example, Here is our main script(Parent Script)

// main.js
let worker = new Worker('web-worker.js');
worker.addListener('message',(e) => {
    console.log('Hey! Workers Said : ', e.data);
},false);
worker.postMessage('This message is from Parent to Web Worker');

Our web worker code will look something like this,

// web-worker.js
self.addEventListener('message', function(e) {
  self.postMessage(e.data);
}, false);

Explanation

Here postMessage() from main.js sends the message to the web worker, and our worker handles that message by defining onmessage event and vice versa.

Let's write a simple program, We will have three buttons,START THE WROK POST MESSAGE, and STOP THE WORK

// index.html
<!DOCTYPE html>
<body>
    <div class="wrapper">
        <div class="buttonWrapper">
            <button style="background-color: green;" onclick="startWork()">Start The Work</button>
            <button style="background-color: yellow;" onclick="sendMessage()">Send The Message</button>
            <button style="background-color: red;" onclick="stopWork()">Stop The Work</button>
            <div class="messageWarpper">Message: <span id="workerMessage">No Message for now!</span>
            </div>

        </div>
    </div>
</body>

The main.js, which will be our parent for web worker will look something like this,


var worker;
function startWork() {
    if (window.Worker) { // just to check browser compatibility
        worker = new Worker('./worker.js');
        worker.onmessage = function (e) {
            document.getElementById('workerMessage').innerHTML = e.data.message;
        };
        worker.postMessage({ 'message': 'Hey Buddy Start the work' });
    }
}
function sendMessage() {
    if (worker) {
        worker.postMessage({ 'message': 'Hey, You Done?' });
    } else {
        document.getElementById('workerMessage').innerHTML = "Please start the worker";
    }
}
function stopWork() {
    if (worker) {
        worker.postMessage({ 'message': 'That is okay! You can stop the work' });
        setTimeout(() => worker.terminate(), 100);
    } else {
        document.getElementById('workerMessage').innerHTML = "Work has stopped. Already please start again";
    }

}

And our worker.js will be something like this...

self.onmessage = function (e) {
    if (e.data.message == 'Hey Buddy Start the work') {
        self.postMessage({ message: 'Yeah! Work has been started!' });
    } else if (e.data.message == 'Hey, You Done?') {
        self.postMessage({ message: 'Yeah,Completed!' });
    } else if (e.data.message == 'That is okay! You can stop the work') {
        self.postMessage({ message: 'Worked Stopped. Please tell me when to start' });
    }
}

Here Starting the work means we will register our web worker with the client, and the send message will send the message to the worker, in response web worker can sending the appropriate message.

In our main.js we are continuously listening for the message from the worker. We can stop the work using the worker.terminate() API, I have added a delay of 100ms to get the response from the worker as well.

Here how it actually looks like,

Working Example Recoding Get the full source code here.

This is it about this article, please do tell me if you liked this article in the comments and to get notified about my next article do follow me, till then You can connect me on Twitter or DM on Discord .

Did you find this article valuable?

Support Adarsh Thakur by becoming a sponsor. Any amount is appreciated!