Web Workers and Multithreading

Rajeev R
6 min readAug 16, 2021

--

Web worker is a browser API facilitating multi-threaded architecture which makes it possible to execute multiple JavaScript code/task in parallel.

Before proceeding further with web workers, lets briefly understand how asynchronous JavaScript works and what are their limitations? how web workers can helps to overcome these limitations?

We all know that JavaScript is a single threaded language.It means, JavaScript engine has single call stack and event loop. And the code execution happens line by line.

JavaScript Engine and web API

If there is a CPU hungry block of code running in single thread, your browser will stop responding until the code execution finishes. This brings a huge trade-off for web users.

A study of web page load time and performance shows that if a page load time is more than 3 seconds, 80 % of the users abandons the website.

In today’s era of complex web application and increased rate of mobile internet traffic, it is quite challenging to design a UI rich web application which loads in less than 3 seconds.

To design a performant non-blocking web application we have been using browser web APIs such as Fetch API, AJAX, setTimeout etc. to perform asynchronous task. These API operates on its own thread which runs in parallel with main thread( JavaScript Runtime).

Lets see an example of timeout API

console.log(“log1”)setTimeout(()=>{console.log(“log2”), 1000})console.log(“log3”)//OUTPUT : log1 log3 log2

First , console.log(“log1”) goes into stack and executes.

Next , setTimeout() goes into stack. When JavaScript engine see a setTimeout, it send it to timeout API and console.log(“log3”) enters the call stack .

Timeout API starts in a separate thread and waits for a second before it adds the callback function in callback queue. setTimeout runs in parallel with main call Stack thread console.log(“log3”) is being executed.When callback queue has some task(callback function) , the event loop keeps polling the call stack for it to become empty

When console.log(“log3”) is executed, call stack gets empty and event loop pushes the callback function (console.log(“log2”)) from callback queue to call stack and execution continues.

That’s the way asynchronous JavaScript execution happens . But even though the Web APIs do some work in parallel on different thread, the result of the work(callback functions) indeed, gets executed in main thread.

What if the callback is a CPU hungry block of code something like below

for(let i = 0; i <9999999999; i++){  //some CPU hungry js code}

When this code is pushed to call stack, your browser will get unresponsive until the for loop execution finishes.

Even if we try to execute above code using setTimeout, the callback loop will be executed inside call stack after timeout.

Therefore one can say that even the asynchronous operation can block the DOM and asynchronous JavaScript is not a truly multi threaded JavaScript environment.

Now Here comes the magic of the Web Worker API introduced in HTML5.

With web workers, it is now possible to have multiple JS threads running in parallel. It allows the browser to have a normal operation with the event loop based execution of the single main thread on the user side, while making room for multiple threads in the background. Each web worker has a separate message queue, event loop, and memory space independent from the original thread that instantiated it.(see image below)

Web workers communicate with the main document/ the main thread via message passing technique. The message passing is done using the postMessage API.

every web worker has its own event loop.

Lets see an example of web worker first

                            main.js    let name = "bubble sort";let items = [43,67,12,76,43,98,43,1,0,8,4,7,15,34,76,98,22,11,77];console.info(name)var worker = new Worker('worker.js');worker.postMessage(items);worker.onmessage = function (event) {    console.info("The sorted data: " + event.data);};worker.onerror = function (event) {    console.error(event.message, event);};console.info(name)//other JavaScript code goes down
worker.jsonmessage = function(e) {

const sortedItems = bubblesort(e.data);
postMessage(sortedItems);};function bubblesort(items = [1, 2, 3, 4, 5]) { const length = items.length; for (let i = 0; i < length; i++) { let optimizer = 0; for (let j = 0; j < (length - i - 1); j++) { if (items[j] > items[j + 1]) { [items[j], items[j + 1]] = [items[j + 1], items[j]]; optimizer++; } } if(!optimizer){ break; } } return items;}

In the above example (main.js) line 1 , 2 and 3 are executed by JavaScript engine as usual. At line 4, a worker object is created using Worker constructor function .The worker construction function takes a parameter which is a JavaScript file (worker.js)

At this point, As web worker is not part of JavaScript, JavaScript engine informs the browser to spawn a worker thread and work with worker.js.

And then console.Info(name) is executed by JavaScript engine.

On the other hand, in parallel with JavaScript execution, browser spawn a new worker thread and downloads the worker.js file via asynchronous HTTP request.

Right after the worker.js is downloaded, it will get executed and the worker will begin. How?

To start the worker and JavaScript execution, JavaScript engine sends postMessage() command to browser. See line number 5 of main.js

Post message can have arguments (string/json) for worker.js to work with.

And Hurrah!, your worker thread is started and executing worker.js.

Now lets look at worker.js

Inside worker.js we have a big for-loop. After the loop finishes , worker thread sends the result to main js thread via postMessage() function

It’s the same postMessage() function which was used in main thread to send command and initialize worker thread.

i.e postMessage() function is used to communicate with worker thread and main thread.

The worker threads are OS-level threads and it consume system resources.

one can run any JS code on worker thread except some exception as:

  1. you cannot access DOM object inside worker thread.
  2. you cannot access global variables and JavaScripts function form main thread(i.e. web page)

3. you cannot access some of the items available on windows object.

Types of web Worker

There are two types of web worker

1. Dedicated web worker

2. Shared web worker

The example which we have seen so far, is the example of dedicated web worker. Dedicated web worker are dedicated to a web page (main thread: main.js), i.e they are created by a specific web page and serves only its creator. if the creator web page dies, worker also dies.

on the other hand shared workers created by a web page are shared across multiple web pages. shared workers dies when all other web pages ,which uses workers, dies.

Shared workers are bit complex to code than dedicated workers.you may dig deep into these type on the link above.

Uses of web worker

web workers are amazingly helpful when one has to optimize their web application and improve the data load time.you can use workers to prefetch and store the data in advance and use it when needed during web app life cycle, without impacting web UI.

Other use cases where web workers must be used are :

  1. network requests and returned data processing.
  2. processing large arrays/JSON
  3. large data manipulations.
  4. complex calculations and so on ..

Browsers support

At the time of writhing this, web workers are supported by all major browsers latest version. however you may check the support for specific details such as shared worker support, dedicated worked support @here.

Browser support

--

--

Rajeev R

A Javascript enthusiast , A web developer, A passionate cosmologist.