Web Workers run JavaScript on a separate thread, letting expensive computations happen without blocking the main thread that handles rendering and user interaction ā the only way to achieve true parallelism in browser JavaScript.
1Introduction to Web Workers | JavaScript Tutorial - In-Depth Guide Part 1
A Web Worker runs its JavaScript file on a completely separate thread, in parallel with the main thread ā a long computation in a worker never blocks the page from rendering or responding to clicks.
const worker = new Worker('heavy-computation.js');
worker.postMessage({ numbers: largeArray });True Parallelism
2Introduction to Web Workers | JavaScript Tutorial - In-Depth Guide Part 2
The main thread and a worker communicate exclusively through postMessage() and the 'message' event ā they don't share memory or variables directly.
// Main thread:
worker.postMessage({ cmd: 'process', data });
worker.onmessage = (e) => console.log('Result:', e.data);
// Inside worker.js:
self.onmessage = (e) => {
const result = process(e.data.data);
self.postMessage(result);
};postMessage Communication
3Introduction to Web Workers | JavaScript Tutorial - In-Depth Guide Part 3
Workers have no access to the DOM, window, or document ā they run in a restricted global scope with a different set of available APIs.
// Inside a worker ā these all throw or are unavailable:
document.querySelector('.x'); // ReferenceError: document is not defined
window.alert('hi'); // ReferenceErrorNo DOM Access
4Introduction to Web Workers | JavaScript Tutorial - In-Depth Guide Part 4
Web Workers are worth the added complexity specifically for CPU-intensive work ā heavy computation, large data processing, image/video manipulation ā not for I/O-bound work like network requests, which async/await already handles without blocking.
// Good fit for a worker: heavy synchronous computation
function computeFractal(params) { /* CPU-intensive loop */ }
// NOT a good fit: fetch() is already non-blocking on the main threadWhen Workers Are Worth It
5Introduction to Web Workers | JavaScript Tutorial - In-Depth Guide Part 5
Always terminate a worker with worker.terminate() (from the main thread) or self.close() (from inside the worker) once it's no longer needed, to free the resources of that separate thread.
// When the feature no longer needs the worker:
worker.terminate();Terminating Workers
6Step-by-Step Breakdown
A Web Worker runs its JavaScript file on a completely separate thread, in parallel with the main thread ā a long computation in a worker never blocks the page from rendering or responding to clicks.
The main thread and a worker communicate exclusively through postMessage() and the 'message' event ā they don't share memory or variables directly.
Checkpoint: Does data sent via postMessage() get shared by reference between the main thread and a worker?
- āYes, both sides share the exact same object in memory
- āNo, it is copied using the structured clone algorithm
Workers have no access to the DOM, window, or document ā they run in a restricted global scope with a different set of available APIs.
Web Workers are worth the added complexity specifically for CPU-intensive work ā heavy computation, large data processing, image/video manipulation ā not for I/O-bound work like network requests, which async/await already handles without blocking.
Checkpoint: Is a Web Worker the right tool for making a slow network request "non-blocking"?
- āYes, workers are the standard way to handle any async work
- āNo, fetch() with async/await already handles that without blocking
Always terminate a worker with worker.terminate() (from the main thread) or self.close() (from inside the worker) once it's no longer needed, to free the resources of that separate thread.
Next, we'll explore 'Custom Errors'.
Level Up š
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Keep the Main Thread Free for Timely Accessibility Announcements
Offloading heavy computation to a Web Worker keeps the main thread responsive, which matters for accessibility too ā a blocked main thread can delay ARIA live region announcements and keyboard event handling, degrading the experience for assistive technology users.
SEO Implications
- 1
Offloading Heavy Computation Improves Interaction Responsiveness Metrics
Moving CPU-intensive work off the main thread with a Web Worker can improve Interaction to Next Paint (a Core Web Vital), since the main thread remains free to respond to user input promptly instead of being blocked by synchronous computation.
Best Practices
Reserve Workers for Genuinely CPU-Intensive Synchronous Work
Network requests and other I/O-bound tasks are already non-blocking via Promises on the main thread; workers solve the different problem of keeping the UI responsive during heavy computation.
Terminate Workers When Their Task Is Complete
A worker left running indefinitely after its job is done wastes system resources on a background thread nobody is using anymore.
Frequent Bugs
Reaching for a Web Worker to make a slow fetch() request 'non-blocking', when fetch() is already asynchronous and non-blocking on the main thread ā adding worker complexity for no real benefit.
Reserve workers for CPU-bound computation; use plain async/await for I/O-bound work like network requests.
Trying to directly manipulate the DOM from inside a worker script, causing a ReferenceError since document/window are unavailable there.
Have the worker compute results and postMessage() them back to the main thread, where the actual DOM update happens.
Real-World Examples
Offloading Heavy Image Processing to a Worker
A photo editing feature needed to apply computationally expensive filters to large images without freezing the UI during processing.
// main.js
const worker = new Worker('image-filter-worker.js');
worker.postMessage({ imageData });
worker.onmessage = (e) => renderProcessedImage(e.data.result);
// image-filter-worker.js
self.onmessage = (e) => {
const result = applyExpensiveFilter(e.data.imageData);
self.postMessage({ result });
};