Web Workers and Service Workers
Lately I came across this JS library, MSW (Mock Service Worker) that mocks network requests in development time using Service Workers.
What a neat idea! In the past, we used to build our own mock server of backend APIs but that was not a super nice solution. E.g. we cannot mock requests whose URL contains domain this way. Alternatively, we could stub the http client in frontend code base but that means behavioral changes when switching to production. MSW in this case is much cleaner.
I got to know about web workers and service workers from some Google presentation a few years ago, and later on, in PWA. Nevertheless, I have never built anything using these two types of workers (now looking back, I should have 😄), hence the two understanding of them is rather murky. (The main point I remember was that, given JavaScript is single threaded, these workers are doing work so that DOM is not blocked.) I've then decided to refresh my memory and get a better grasp of what they are.
Service Workers
Service worker is used to intercept network requests, and do something about it. You could
- bypass to actual backend
- fetch from some kind of cache
- do both above (quite popular patterns now in web and mobile apps, where UI is driven by local state, which comes from cache, and cache is always updated from backend when network is available)
There's a lot more to service worker APIs which you can read from tutorial here. But the basic are the following:
// In main app
navigator.serviceWorker
.register("/myServiceWorker.js")
.then(() => {
console.log("service worker registered");
})
.catch(console.error);
// Somewhere in your application to fetch an external API
const fetchData = async () => {
const result = await fetch(
"https://fakerapi.it/api/v1/addresses?_quantity=1"
);
const json = await result.json();
// json.data[0].street will always be `My Mock Street`, as it's intercepted and returned from service worker. See below.
console.log(json);
};
// In myServiceWorker.js - simplified
self.addEventListener("install", (e) => {
console.log("service worker installed", e);
e.waitUntil(self.skipWaiting());
});
self.addEventListener("activate", (e) => {
console.log("service worker activated", e);
e.waitUntil(self.clients.claim());
});
self.addEventListener("fetch", (e) => {
console.log("service worker fetch", e);
// Force to return fake data
if (e.request.url === "https://fakerapi.it/api/v1/addresses?_quantity=1") {
e.respondWith(
new Response(
`{"status":"OK","code":200,"total":1,"data":[{"id":1,"street":"My Mock Street"}]}`
)
);
}
});
Lifecycle
Service Worker usually is one per domain, and it can outlive the tab. There is a lot more to service worker than outlined here. I think this article explains it very well.
Web Workers
Similar to service workers, we need a separate file for a web worker, and start it from the main app. Here I've just calculated the average value of Math.random()
but you can image that it can be used for other purposes as well.
// In main app
const webWorker = new Worker("/myWebWorker.js");
const startWebWorker = () => {
console.log("posting to web worker");
webWorker.postMessage("Hello worker!");
webWorker.onmessage = (e) => {
console.log("received from web worker", e);
};
};
startWebWorker();
// in myWebWorker.js
const expensiveProcess = () => {
const length = 1000000;
const manyRandoms = Array.from({ length }, () => Math.random());
return manyRandoms.reduce((acc, curr) => acc + curr, 0) / length;
};
self.onmessage = function (e) {
console.log("web worker onmessage", e.data);
self.postMessage(expensiveProcess());
};
Lifecycle
It lives with the tab, quite simply. And you can have multiple web workers for a given tab.
To destroy a web worker:
webWorker.terminate();
Is it required?
I think service workers are quite useful to provide offline support for your app. Web workers, on the other hand, really depends on what kind of web app you're building. If it does lots of heavy calculation and you need to maintain fluidity (maybe with a lot of animations?), then it's worth investing into it. Else for simple sites with just images and texts, I guess I could live without it. Just my two cents.