Web workers! Why and how to use them ?
In this blog, I will discuss about the web workers and try to answer some questions like, Why, What and How can we use them ?
For example :
In the above example, if you call average before hello method, then your page will become unresponsive and you won’t be able to click on Hello till execution of average gets completed.
You can observe that when average is called with 10000 as input first, then it took ~1.82 seconds and for that amount of time page becomes unresponsive and you were not able to click on hello button.
A good example of async programming is
XHR request, in this we hit an API asynchronously and while waiting for the response, other code can be executed. But this is limited to certain use cases related to web APIs mostly.
Another way of writing async code is by using
setTimeout method. In some cases, you can achieve good results in unblocking the UI from longer-running computations by using
setTimeout. For example, by batching a complex computation in separate
For example :
async programming using setTimeout
In this example, you will observe that after you click on Calculate Average button, you can still click on Hello button which in turn shows alert message. This way of programming surely non-blocking but takes too much amount of time, which is not feasible in real world applications.
Here, for the same input 10000, it took ~60 seconds, which is very much inefficient.
How do we solve these kinds of issues efficiently ?
Answer is Web Workers
What are web workers ?
Web workers are created by a constructor function Worker() which runs a named JS file.
If the specified file exists then it will be downloaded asynchronously and if not then worker will fail silently, so your application will still work in case of 404.
We will learn more about creation and working of web workers in the next section.
Worker thread has its own context and therefore you can only access selected features inside a worker thread like – web sockets, indexed DB.
There are some restrictions with web workers –
- You can’t directly manipulate the DOM from inside a worker.
- You can not use some default methods and properties of the window object since window object is not available inside a worker thread.
- The context inside the worker thread can be accessed via DedicatedWorkerGlobalScope or SharedWorkerGlobalScope depending upon the usage.
Features of Web Workers
There are two types of web workers –
- Dedicated web worker – A dedicated worker is only accessible by the script that called it.
- Shared web worker – A shared worker is accessible by multiple scripts — even if they are being accessed by different windows, iframes or even workers.
Let us discuss more about those two types of web workers –
Creation of a web worker :
Creation is pretty much same for both Dedicated and Shared web worker.
- Dedicated web worker Creating a new worker is simple, just call the Worker constructor and pass the path of the script you want to execute as worker.
- Shared web worker : Creating a new shared worker is pretty much the same as that of dedicated worker, but with a different constructor name.
Communication between main and worker thread :
Communication between main thread and worker thread happens via postMessage method and onmessage event handler.
- Dedicated web worker In case of a dedicated web worker, communication system is simple. You just need to use postMessage method whenever you want to send message to the worker.
and inside a web worker you can respond when the message is received by writing an event handler block like this –
onmessage handler allows to run some code whenever a message is received. Here we are calculating average of numbers and then use
postMessage() again, to post the result back to the main thread. As you can see on line 6 in main.js we have used onmessage event on the worker instance. So whenever worker thread use postMessage, onmessage in the main thread gets triggered.
- Shared web worker In case of a shared web worker, communication system is little different. As one worker is shared between multiple scripts, we need to communicate via the port object of worker instance. This is done implicitly in case of dedicated workers. You need to use postMessage method whenever you want to send message to the worker.
and inside a web worker (main-shared-worker.js) it is a little complex.
First, we use an
onconnect handler to fire code when a connection to the port happens (line 2).
We use the
ports attribute of this event object to grab the port and store it in a variable (line 4).
Next, we add a
message handler on the port to do the calculation and return the result to the main thread (line 7 and line 25) like this –
Termination of a web worker :
If you need to immediately terminate a running worker from the main thread, you can do so by calling the worker’s terminate method :
The worker thread is killed immediately without an opportunity to complete its operations.
Spawning of web worker
Workers may spawn more workers if they wish. But they must be hosted within the same origin as the parent page.
Worker threads have access to a global function,
importScripts(), which lets them import scripts.
We have discussed some of the approaches above to achieve async programming so that our UI doesn’t get blocked due to any heavy computational task. But there are some limitations to those approaches. So we can use web workers to solve these kind of problems efficiently.
Click here to run this live demo
Here, you will see 3 sections –
- Blocking Code : When you click on calculate average, loader does not displayed and after some time you see the final result and time taken. This is because as soon as average method gets called, I have triggered showLoader method also but as JS is single threaded, it won’t execute showLoader till the execution of average gets completed. So, you won’t be able to see the loader in this case ever.
- Async Code : In this I tried to achieve the same functionality by using setTimeout method and putting every function execution into an event loop. You will see loader in this case but the response would take time as compared to the method defined above.
- Web worker : This is an example of using a web worker. In this you will see loader as soon as you click on calculate average and you will get response in same time as of method 1, for the same number.
You can access the source code for the same – here
There are some advance concepts related to web workers, like –
- Content Security Policy – Web workers have their own execution context independent of the document that created them and because of this reason they are not governed by the Content Security Policy of the parent thread/worker. The exception to this is if the worker script’s origin is a globally unique identifier (for example, if its URL has a scheme of data or blob). In this case, the worker inherit the content security policy of the document or worker that created it.
- Transferring data to and from workers – Data passed between main and worker thread is copied and not shared. Objects are serialized as they’re handed to the worker, and subsequently, de-serialized on the other end. The page and worker do not share the same instance, so the end result is that a duplicate is created on each end. Browsers implemented Structured Cloning algorithm to achieve this.
- Embedded workers – You can also embed the code of worker inside a web page (html). For this you need to add a script tag without a src attribute and assign a non-executable MIME type to it, like this –
There can be a lot of use cases to use web workers in our application. I have just discussed a small scenario. Hope this helps you understanding the concept of web workers.
Github Repo : https://github.com/bhushangoel/webworker-demo-1
Web worker in action : https://bhushangoel.github.io/webworker-demo-1/
JS demo showcase : https://bhushangoel.github.io/
Thank you for reading.
Happy Learning 🙂