Leer wat Web Workers en Service Workers zijn, hoe ze verschillen en wanneer je welke inzet voor snellere pagina’s, achtergrondtaken, caching en offline-ondersteuning.

Browsers draaien het grootste deel van je JavaScript op de hoofdthread—dezelfde plek die gebruikersinput, animaties en het schilderen van de pagina afhandelt. Als daar zware taken plaatsvinden (grote data parsen, beeldverwerking, complexe berekeningen), kan de UI haperen of “bevriezen”. Workers bestaan om bepaalde taken van de hoofdthread af te halen of buiten de directe controle van de pagina te laten lopen, zodat je app responsief blijft.
Als je pagina bezig is met een berekening van 200 ms, kan de browser niet soepel scrollen, op klikken reageren of animaties op 60fps houden. Workers helpen door achtergrondwerk mogelijk te maken terwijl de hoofdthread zich op de interface richt.
Een Web Worker is een achtergrond-JavaScript-thread die je vanuit een pagina maakt. Hij is het meest geschikt voor CPU-intensieve taken die anders de UI zouden blokkeren.
Een Service Worker is een speciaal soort worker die tussen je webapp en het netwerk zit. Hij kan requests onderscheppen, responses cachen en features zoals offline ondersteuning en pushmeldingen mogelijk maken.
Zie een Web Worker als een helper die berekeningen in een andere kamer uitvoert. Je stuurt een bericht, hij werkt en stuurt terug.
Zie een Service Worker als een poortwachter bij de voordeur. Requests voor pagina’s, scripts en API-calls lopen langs hem, en hij kan beslissen of hij van het netwerk haalt, uit de cache serveert of op een aangepaste manier reageert.
Aan het einde weet je:
postMessage) binnen het worker-model past, en waarom de Cache Storage API belangrijk is voor offlineDit overzicht schetst de “waarom” en het mentale model—daarna duiken we dieper in hoe elk workertype zich gedraagt en waar het in echte projecten past.
Als je een webpagina opent, gebeurt het meeste van wat je “voelt” op de hoofdthread. Die is verantwoordelijk voor pixels tekenen (renderen), reageren op taps en klikken (input) en veel JavaScript uitvoeren.
Omdat renderen, input-afhandeling en JavaScript vaak om beurten op dezelfde thread draaien, kan één trage taak alles laten wachten. Daarom verschijnen performanceproblemen vaak als responsiveness-issues, niet alleen als “langzame code”.
Wat “blokkeren” voor gebruikers voelt als:
JavaScript heeft veel asynchrone API’s—fetch(), timers, events—die je helpen idly wachten te vermijden. Maar async maakt zware taken niet automatisch tegelijkertijd met renderen mogelijk.
Als je dure berekeningen doet (beeldverwerking, groot JSON-crunchen, crypto, complexe filtering) op de hoofdthread, concurreert dat nog steeds met UI-updates. “Async” kan uitstellen wanneer het draait, maar het kan nog steeds op dezelfde hoofdthread draaien en jank veroorzaken wanneer het uitgevoerd wordt.
Workers bestaan zodat browsers de pagina responsief kunnen houden terwijl ze toch betekenisvol werk uitvoeren.
Kort: workers beschermen de hoofdthread zodat je app interactief blijft terwijl er op de achtergrond echt werk gebeurt.
Een Web Worker is een manier om JavaScript weg van de hoofdthread uit te voeren. In plaats van te concurreren met UI-werk (renderen, scrollen, reageren op klikken), draait een worker in zijn eigen achtergrondthread zodat zware taken kunnen afronden zonder de pagina “vast” te zetten.
Zie het zo: de pagina blijft gericht op gebruikersinteractie, terwijl de worker CPU-intensieve taken afhandelt zoals het parsen van een groot bestand, nummers kraken of data voorbereiden voor grafieken.
Een Web Worker draait in een afgescheiden thread met zijn eigen globale scope. Hij heeft nog wel toegang tot veel web-API’s (timers, fetch in veel browsers, crypto, enz.), maar is opzettelijk geïsoleerd van de pagina.
Er zijn een paar vaak voorkomende varianten:
Als je nog nooit met workers hebt gewerkt, zijn de meeste voorbeelden die je ziet Dedicated Workers.
Workers roepen niet direct functies in je pagina aan. Communicatie gebeurt via berichten:
postMessage().postMessage().Voor grote binaire data kun je vaak de prestatie verbeteren door eigendom van een ArrayBuffer over te dragen (zodat het niet wordt gekopieerd), waardoor message passing snel blijft.
Omdat een worker geïsoleerd is, zijn er een paar belangrijke beperkingen:
window of document. Workers draaien onder self (een worker global scope), en beschikbare API’s kunnen verschillen van de hoofdpagina.Goed gebruikt is een Web Worker een van de eenvoudigste manieren om de prestaties van de hoofdthread te verbeteren zonder te veranderen wat je app doet—alleen waar het zware werk gebeurt.
Web Workers zijn erg geschikt wanneer je pagina “vastloopt” omdat JavaScript te veel werk op de hoofdthread doet. De hoofdthread is ook verantwoordelijk voor gebruikersinteracties en rendering, dus zware taken daar kunnen jank, vertraagde klikken en vastlopende scrolls veroorzaken.
Gebruik een Web Worker wanneer je CPU-intensief werk hebt dat geen directe toegang tot de DOM nodig heeft:
Een praktisch voorbeeld: als het parsen van een groot JSON-payload de UI doet stagneren, verplaats het parsen naar een worker en stuur daarna het resultaat terug.
Communicatie met een worker gebeurt via postMessage. Voor grote binaire data geef je de voorkeur aan transferable objects (zoals ArrayBuffer) zodat de browser het geheugen kan overdragen in plaats van kopiëren:
// main thread
worker.postMessage(buffer, [buffer]); // transfers the ArrayBuffer
Dit is vooral nuttig voor audio-buffers, afbeeldingsbytes of andere grote datachunks.
Workers hebben overhead: extra bestanden, message passing en een andere debugflow. Sla ze over als:
postMessage ping-pong kan het voordeel opheffen.Als een taak een merkbare pauze kan veroorzaken (vaak ~50ms+) en kan worden uitgedrukt als “input → compute → output” zonder DOM-toegang, is een Web Worker meestal de moeite waard. Als het vooral om UI-updates gaat, houd het op de hoofdthread en optimaliseer daar.
Een Service Worker is een speciaal JavaScript-bestand dat in de achtergrond van de browser draait en fungeert als een programmeerrbare netwerklaag voor je site. In plaats van in de pagina zelf te draaien, zit hij tussen je webapp en het netwerk, waardoor je kunt beslissen wat er gebeurt wanneer de app resources opvraagt (HTML, CSS, API-calls, afbeeldingen).
Een Service Worker heeft een levenscyclus die losstaat van een enkele tab:
Omdat hij op elk moment gestopt en herstart kan worden, behandel hem als een event-driven script: doe werk snel, sla state op in persistente opslag en veronderstel niet dat hij altijd draait.
Service Workers zijn beperkt tot dezelfde origin (zelfde domein/protocol/poort) en controleren alleen pagina’s binnen hun scope—meestal de map waar het workerbestand wordt geserveerd (en daaronder). Ze vereisen ook HTTPS (behalve localhost) omdat ze netwerkrequests kunnen beïnvloeden.
Een Service Worker wordt vooral gebruikt om tussen je webapp en het netwerk te zitten. Hij kan beslissen wanneer het netwerk te gebruiken, wanneer gecachte data te leveren en wanneer wat achtergrondwerk te doen—zonder de pagina te blokkeren.
De meest voorkomende taak is offline of “slechte verbinding”-ervaringen mogelijk maken door assets en responses te cachen.
Een paar praktische caching-strategieën die je zult tegenkomen:
Dit wordt meestal geïmplementeerd met de Cache Storage API en fetch event handling.
Service Workers kunnen de waargenomen snelheid op terugkerende bezoeken verbeteren door:
Het resultaat is minder netwerkverzoeken, snellere opstart en consistentere prestaties bij flakkerende verbindingen.
Service Workers kunnen achtergrondmogelijkheden aandrijven zoals pushmeldingen en background sync (ondersteuning varieert per browser en platform). Dat betekent dat je gebruikers kunt notificeren of een mislukte aanvraag later opnieuw kunt proberen—ook als de pagina niet open is.
Als je een progressive web app bouwt, zijn Service Workers een kernonderdeel achter:
Als je maar één ding onthoudt: Web Workers helpen je pagina zware taken doen zonder de UI te laten bevriezen, terwijl Service Workers je app helpen netwerkrequests te controleren en zich te gedragen als een installable app (PWA).
Een Web Worker is bedoeld voor CPU-intensieve taken—groot data parsen, thumbnails genereren, nummers kraken—zodat de hoofdthread responsief blijft.
Een Service Worker is bedoeld voor request-afhandeling en lifecycle-taken—offline ondersteuning, cachingstrategieën, background sync en pushmeldingen. Hij kan zich tussen je app en het netwerk nestelen.
Een Web Worker is typisch gekoppeld aan een pagina/tab. Als de pagina verdwijnt, gaat de worker meestal ook weg (tenzij je speciale gevallen gebruikt zoals SharedWorker).
Een Service Worker is event-driven. De browser kan hem starten om een event af te handelen (zoals een fetch of push), en hem stoppen als hij idle is. Dat betekent dat hij kan draaien ook als geen tab open is, zolang een event hem wakker maakt.
Een Web Worker kan netwerkrequests doen via fetch(), maar kan de network stack van de pagina niet onderscheppen of herschrijven. Hij kan geen responses voor andere delen van je site cachen of serveerbaar maken.
Een Service Worker kan netwerkrequests onderscheppen (via het fetch event), beslissen of hij naar het netwerk gaat, antwoordt vanuit cache of een fallback terugstuurt.
Een Web Worker beheert niet de HTTP-caching van je app.
Een Service Worker gebruikt vaak de Cache Storage API om request/response-paren op te slaan en te serveren—de basis voor offline caching en “directe” herladingen.
Een worker laten draaien gaat vooral over waar hij draait en hoe hij geladen wordt. Web Workers worden direct door een pagina gemaakt. Service Workers worden door de browser geïnstalleerd en komen “voor” netwerkrequests te staan.
Een Web Worker begint wanneer je pagina er één creëert. Je verwijst naar een apart JavaScript-bestand en communiceert via postMessage.
// main.js (running on the page)
const worker = new Worker('/workers/resize-worker.js', { type: 'module' });
worker.postMessage({ action: 'start', payload: { /* ... */ } });
worker.onmessage = (event) => {
console.log('From worker:', event.data);
};
Een goed mentaal model: het workerbestand is gewoon een andere script-URL die je pagina kan ophalen, maar hij draait buiten de hoofdthread.
Service Workers moeten worden geregistreerd vanaf een door de gebruiker bezochte pagina:
// main.js
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
Na registratie regelt de browser de install/activate-levenscyclus. Je sw.js kan luisteren naar events zoals install, activate en fetch.
Service Workers kunnen netwerkrequests onderscheppen en responses cachen. Als registratie over HTTP was toegestaan, zou een aanvaller op het netwerk een kwaadaardige sw.js kunnen injecteren en toekomstige bezoeken effectief kunnen overnemen. HTTPS (of http://localhost tijdens ontwikkeling) beschermt het script en het verkeer dat het kan beïnvloeden.
Browsers cachen en updaten workers anders dan normale pagina-scripts. Plan updates:
sw.js/worker-bundle te deployen).Als je later een soepelere rollout-strategie wilt, zie /blog/debugging-workers voor testgewoonten die update edge cases vroeg opsporen.
Workers falen anders dan “normale” pagina-JavaScript: ze draaien in aparte contexten, hebben hun eigen console en kunnen door de browser herstart worden. Een goede debugroutine bespaart uren.
Open DevTools en zoek naar worker-specifieke targets. In Chrome/Edge zie je workers vaak onder Sources (of via de “Dedicated worker” entry) en in de Console context-selector.
Gebruik dezelfde tools als op de hoofdthread:
onmessage-handlers en langlopende functies.Als berichten “kwijt” lijken te raken, inspecteer beide kanten: verifieer dat je worker.postMessage(...) aanroept, dat de worker self.onmessage = ... heeft en dat de berichtvorm overeenkomt.
Service Workers debug je het beste in het Application-paneel:
Kijk ook in de Console voor install/activate/fetch-fouten—die verklaren vaak waarom caching of offline-gedrag niet werkt.
Caching-problemen zijn de grootste tijdvreter: het cachen van de verkeerde bestanden (of te agressief) kan oude HTML/JS blijven serveren. Tijdens tests: doe een hard reload en controleer wat er daadwerkelijk uit de cache komt.
Voor realistische tests gebruik DevTools om:
Als je snel iterateert op een PWA, helpt het vaak om te beginnen met een schone basisapp (met een voorspelbare Service Worker en build-output) en van daar cachingstrategieën te verfijnen. Platforms zoals Koder.ai kunnen nuttig zijn voor dit soort experimenten: je kunt een React-gebaseerde webapp prototypen vanuit een chatprompt, de broncode exporteren en daarna je worker-setup en cachingregels verfijnen met een kortere feedbackloop.
Workers kunnen apps vloeiender en capabeler maken, maar ze veranderen ook waar code draait en wat het kan benaderen. Een korte controle op beveiliging, privacy en prestaties voorkomt verrassende bugs—en ontevreden gebruikers.
Zowel Web Workers als Service Workers zijn beperkt door het same-origin policy: ze kunnen alleen direct met resources van dezelfde scheme/host/poort interacteren (tenzij de server cross-origin toegang expliciet toestaat via CORS). Dit voorkomt dat een worker ongemerkt data van een andere site binnenhaalt en in je app mengt.
Service Workers hebben extra beveiligingsmaatregelen: ze vereisen meestal HTTPS (of localhost tijdens ontwikkeling) omdat ze netwerkrequests kunnen onderscheppen. Behandel ze als bevoegd code: houd afhankelijkheden beperkt, vermijd dynamische code-injectie en versioneer je cachinglogica zorgvuldig zodat oude caches geen verouderde bestanden blijven serveren.
Achtergrondfeatures moeten voorspelbaar aanvoelen. Pushmeldingen zijn krachtig, maar toestemming prompts zijn makkelijk te misbruiken.
Vraag alleen toestemming wanneer er een duidelijk voordeel is (bijv. nadat een gebruiker meldingen in instellingen heeft aangezet) en leg uit wat ze zullen ontvangen. Als je data op de achtergrond synchroniseert of prefetcht, communiceer dat in gewone taal—gebruikers merken onverwachte netwerkactiviteit of meldingen op.
Workers zijn niet “gratis” in termen van performance. Overmatig gebruik kan tegen je werken:
postMessage-calls (vooral met grote objecten) kunnen een bottleneck worden. Geef de voorkeur aan batching en transferables waar geschikt.Niet elke browser ondersteunt elke capability (of gebruikers blokkeren permissies). Feature-detect en degradeer netjes:
if ('serviceWorker' in navigator) {
// register service worker
} else {
// ga door zonder offline features
}
Het doel: kernfunctionaliteit moet blijven werken, met “nice-to-haves” (offline, push, zware berekeningen) als extra lagen wanneer beschikbaar.
Web Workers en Service Workers lossen verschillende problemen op, dus ze vullen elkaar goed aan wanneer een app zowel zware berekeningen als snel, betrouwbaar laden nodig heeft. Een goed mentaal model is: Web Worker = compute, Service Worker = netwerk + caching, hoofdthread = UI.
Stel: je app laat gebruikers foto’s bewerken (schalen, filters, achtergrond verwijderen) en later een galerij bekijken zonder verbinding.
Deze “compute then cache”-aanpak houdt verantwoordelijkheden duidelijk: de worker produceert outputs en de service worker beslist hoe die op te slaan en te serveren.
Voor apps met feeds, formulieren of velddata:
Zelfs zonder volledige background sync verbetert een service worker de waargenomen snelheid door gecachte responses te serveren terwijl de app op de achtergrond bijwerkt.
Vermijd het mengen van rollen:
postMessage).Nee. Een Service Worker draait op de achtergrond, los van enige paginatab, en heeft geen directe toegang tot de DOM (de HTML-elementen van de pagina).
Die scheiding is bedoeld: Service Workers moeten kunnen blijven werken wanneer geen pagina open is (bijv. om op een push-event te reageren of gecachte bestanden te serveren). Omdat er mogelijk geen actief document is om te manipuleren, houdt de browser ze geïsoleerd.
Als een Service Worker moet beïnvloeden wat een gebruiker ziet, communiceert hij met pagina’s via messaging (bijv. postMessage) zodat de pagina de UI kan bijwerken.
Nee. Web Workers en Service Workers zijn onafhankelijke features.
Je kunt één van beide alleen gebruiken, of ze samen inzetten als je zowel achtergrondnetwerk als achtergrondcompute nodig hebt.
In moderne browsers zijn Web Workers breed ondersteund en meestal het veiligere basiskeuze.
Service Workers zijn ook breed beschikbaar in actuele versies van grote browsers, maar er zijn meer vereisten en edge-cases:
localhost tijdens ontwikkeling).Als brede compatibiliteit belangrijk is, behandel Service Worker-features als progressive enhancement: bouw eerst een goede kernervaring en voeg offline/push toe waar beschikbaar.
Niet automatisch.
De echte winst komt door de juiste worker te gebruiken voor het juiste knelpunt en te meten voor en na.
Gebruik een Web Worker wanneer je CPU-intensieve taken hebt die kunnen worden uitgedrukt als input → compute → output en geen toegang tot de DOM nodig hebben.
Goede toepassingen zijn het parsen/transformeren van grote payloads, compressie, crypto, beeld-/audioverwerking en complexe filteringen. Als het werk vooral UI-updates of frequente DOM-lees-/schrijfbewerkingen bevat, zal een worker niet helpen (en kan de DOM toch niet benaderen).
Gebruik een Service Worker wanneer je netwerkcontrole nodig hebt: offline ondersteuning, cache-strategieën, snellere terugkerende bezoeken, request-routing en (waar ondersteund) push/background sync.
Als je probleem is “de UI bevriest tijdens rekenen”, is dat een Web Worker-zaak. Als je probleem is “laden is traag/offline werkt niet”, is dat een Service Worker-zaak.
Nee. Web Workers en Service Workers zijn onafhankelijk.
Je kunt één van beide alleen gebruiken, of ze combineren als je zowel compute- als offline-/netwerkfeatures nodig hebt.
Vooral scope en levensduur.
fetch) af te handelen, zelfs als geen pagina open is, en weer stoppen als hij idle is.Nee. Web Workers hebben geen window/document-toegang.
Als je de UI moet beïnvloeden, stuur dan data terug naar de hoofdthread via postMessage() en werk de DOM in je paginacode bij. Houd de worker gefocust op pure berekeningen.
Nee. Service Workers hebben ook geen DOM-toegang.
Om invloed op wat de gebruiker ziet uit te oefenen, communiceer je met gecontroleerde pagina's via messaging (bijvoorbeeld met de Clients API + postMessage()), en laat je de pagina de UI bijwerken.
Gebruik postMessage() aan beide zijden.
worker.postMessage(data)self.postMessage(result)Voor grote binaire data heeft de voorkeur transferables (zoals ArrayBuffer) om kopiëren te vermijden:
Service Workers zitten tussen je app en het netwerk en kunnen reageren op requests met de Cache Storage API.
Veelvoorkomende strategieën:
Kies een strategie per type resource (app shell vs API-data), niet één globale regel.
Ja, maar houd verantwoordelijkheden gescheiden.
Een veelgebruikt patroon is:
Dit voorkomt dat UI-logica in achtergrondcontexten terechtkomt en houdt prestaties voorspelbaar.
Gebruik de juiste DevTools-oppervlakte voor elk.
onmessage, en profileer om te bevestigen dat de hoofdthread responsief blijft.Bij caching-bugs: verifieer altijd wat daadwerkelijk wordt geserveerd (network vs cache) en test offline/throttling.
worker.postMessage(buffer, [buffer]);