Performance is one of the biggest challenges on the Web & especially on Mobile. Running a web app on 60fps is not an easy task.
While there are many factors that affect the performance of your web apps, in this article I’m going to present Web Workers and how you can run fetch in a Web Worker and why you want to do that.
Can we move some of these operations off the main thread?
Enter Web Workers
With web workers, we can move those expensive operations to a background process that’s running on a different thread.
Even though you will most likely not see performance benefits of running fetch in a web worker, this article will serve as an introduction and explainer for Web Workers and Comlink.
Can I access the DOM from a web worker?
Web Workers don’t have access to the DOM because the DOM is not thread safe. Making the DOM accessible to web workers will defeat the purpose of having Web Workers as their implementation will be much slower.
Can I use Web Workers already?
The picture speaks for itself. 😅
GoogleChromeLabs/Comlink makes it easier for us to write an ES2015 class in a web worker and expose it to our main thread.
The only way to communicate between a Web Worker and your main thread is through postMessage. While you don’t necessarily need Comlink to use Web Workers, Comlink makes it much easier to write a class in a web worker and expose it to be used from the main thread.
Comlink uses postMessage under the hood to communicate between the Web Worker & your page.
I believe that Comlink will motivate developers to rely more and more on Web Workers.
Fetch + Comlink
Comlink-fetch allows you to use Fetch in a web worker that is exposed through Comlink.
I recently published version 0.1.2 on npm. Here’s how it works:
Start by installing comlink-fetch from npm:
npm install comlink-fetch --save
Then create a minimal index.html file that loads index.js:
type="module") because I want to be able to
import Comlink from within that index.js
Now create an index.js with the below code:
Here’s a breakdown of what’s going on in this index.js:
- You import Comlink from comlink.es6.js
- You create a new Web Worker from comlink-fetch’s fetch.worker.js
- You create a proxy for that worker using
And then you’re all set to use it! The rest of the code that I presented is a use case where you want to load some additional information in the background, for example posts that you’ll use later on.
Loading them in a web worker will make sure that they won’t block the main thread and won’t slow down your UI.
It’s worth noting that
GET /posts/1 and
GET /posts/2 are being executed at the same time, in the background.
How Comlink-fetch is built
Once you take a look at the source code of comlink-fetch’s fetch.worker.js, you’ll understand why Comlink is awesome.
I simply defined a class
Fetch which is a wrapper on top of fetch that makes it possible to have nice features, such as setting a base url, default headers and default body.
You only have to import Comlink at the beginning and then expose the class you wrote so that it can be proxied from the the main thread later on.
Take a look at the signature of the class Fetch that I defined:
As you can see there’s no boilerplate or unnecessary code. Just a simple import at the beginning, and a Comlink.expose at the end.
Will you directly see performance benefits if you use comlink-fetch? Most probably not. As outlined in this Stackoverflow thread by Konrad Dzwinel, a generic worker based fetch will not necessarily improve your performance.
However, if you have to do some heavy processing on fetched data, then it would make sense as you’re parsing results and processing them off the main thread.