LEARNING OBJECTIVES ⌵
- Differentiate between the browser
Windowscope and theDedicatedWorkerGlobalScope(self/globalThis). - Identify which Web APIs are fully supported in workers (
fetch,IndexedDB,WebSockets,crypto.subtle,WebAssembly). - Explain why synchronous storage APIs (
localStorage,sessionStorage) and the DOM (document) are forbidden in workers. - Import external dependencies into classic workers using synchronous
importScripts(). - Author modular workers using ES Module syntax (
import/export) via{ type: 'module' }.
📖 The Mental Model & Story (Intuitive Foundation)
Imagine a deep-sea submarine research vessel sent on an expedition miles beneath the ocean surface.
THE SUBMARINE ISOLATION ANALOGY
Surface Headquarters (Window / Main Thread) Submarine (DedicatedWorkerGlobalScope)
+-----------------------------------------+ +-----------------------------------------+
| - Controls the TV Monitors (DOM / UI) | | - No TV Monitors (No DOM access) |
| - Controls Public Windows (window) | | - Has High-Speed Satellite Link (fetch) |
| - Manages Filing Cabinets (localStorage)| | - Has Onboard Hard Drives (IndexedDB) |
| - Interacts with Visitors (alert/prompt)| | - Has Nuclear Math Engine (WebAssembly)|
+-----------------------------------------+ +-----------------------------------------+
\ /
===== [ Radio Link (postMessage) ] =====
The submarine does not have windows overlooking the surface city, nor does it have television monitors connected to the lobby display (no DOM or document). It cannot tap the building receptionist on the shoulder to ask a question (alert()).
However, the submarine is an autonomous high-tech laboratory:
- It has its own high-speed communication dish to fetch raw data directly from external servers (
fetch(),WebSockets). - It has an onboard multi-terabyte database room (
IndexedDB). - It has specialized cryptographic supercomputers (
crypto.subtle) and high-speed binary engines (WebAssembly).
The submarine doesn't need to touch the surface monitor directly; it crunches gigabytes of oceanographic data and radios the summarized findings back to headquarters.
Technical Deep Dive & Specifications
Global Scope Hierarchy: Window vs. WorkerGlobalScope
EventTarget
|
+-------------------+-------------------+
| |
Window WorkerGlobalScope
|
+--------------------+--------------------+
| |
DedicatedWorkerGlobalScope SharedWorkerGlobalScope
Inside a Dedicated Worker, this, self, and globalThis all reference the DedicatedWorkerGlobalScope instance. The identifier window does not exist and evaluates to undefined (or throws ReferenceError).
Comprehensive Web API Support Matrix
| API / Feature | Available in Window? | Available in Web Worker? | Notes / Specification Rationale |
|---|---|---|---|
DOM (document, Element) |
✅ YES | ❌ NO | Prevents concurrent layout reflow thrashing and race conditions. |
window / parent / top |
✅ YES | ❌ NO | No browsing context or frame hierarchy exists in a worker. |
localStorage / sessionStorage |
✅ YES | ❌ NO | Synchronous storage is blocked to prevent cross-thread deadlocks. |
alert() / confirm() / prompt() |
✅ YES | ❌ NO | UI blocking modal dialogs are forbidden off the main thread. |
fetch() / XMLHttpRequest |
✅ YES | ✅ YES | Workers can initiate network requests directly. |
IndexedDB |
✅ YES | ✅ YES | Full asynchronous client-side database access for high-volume storage. |
WebSockets / WebTransport |
✅ YES | ✅ YES | Real-time bi-directional streaming directly to background threads. |
crypto.subtle (Web Crypto) |
✅ YES | ✅ YES | Hardware-accelerated hashing (SHA-256), encryption, and signing. |
WebAssembly (WebAssembly.*) |
✅ YES | ✅ YES | Compiling, instantiating, and executing high-performance C++/Rust binaries. |
OffscreenCanvas |
✅ YES | ✅ YES | 2D and WebGL rendering directly from worker threads. |
setTimeout() / setInterval() |
✅ YES | ✅ YES | Worker event loop supports standard timer queues. |
WorkerLocation (self.location) |
✅ (as Location) |
✅ YES | Read-only access to href, protocol, host, pathname, etc. |
WorkerNavigator (self.navigator) |
✅ (as Navigator) |
✅ YES | userAgent, hardwareConcurrency, language, storage. |
Importing External Scripts in Workers
Method 1: Classic Workers with importScripts()
In classic workers (type: 'classic', default), external scripts are loaded using the synchronous importScripts() global function:
// Inside classic worker (e.g., worker.js)
importScripts('https://cdn.example.com/lodash.min.js', './math-utils.js');
// Scripts execute synchronously in order; global variables are attached to self
const result = _.chunk([1, 2, 3, 4], 2);
importScripts()executes synchronously; it pauses worker execution until each script is downloaded and evaluated.- If a network error occurs, a
NetworkErroris thrown inside the worker.
Method 2: Modern ES Module Workers (type: 'module')
Modern browsers allow Web Workers to be authored as standard ES Modules:
// Main Thread
const worker = new Worker('./worker-module.js', { type: 'module' });
// Inside worker-module.js
import { computeSHA256 } from './crypto-utils.js';
import * as tf from 'https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/+esm';
self.addEventListener('message', async (event) => {
const hash = await computeSHA256(event.data);
self.postMessage({ hash });
});
| Dimension | Classic Worker (importScripts) |
ES Module Worker (import) |
|---|---|---|
| Constructor Option | new Worker('w.js') (default) |
new Worker('w.js', { type: 'module' }) |
| Import Syntax | importScripts('a.js', 'b.js') |
Static import { x } from './x.js' or dynamic import() |
| Evaluation Timing | Synchronous runtime blocking | Asynchronous static module graph resolution |
| Strict Mode | Opt-in via 'use strict' |
Always in Strict Mode automatically |
| CORS Rules | Follows normal script CORS rules | Strict CORS required for module graphs |
💻 Interactive Code Playground
Below is an interactive Worker Capability Inspector & Crypto Engine that queries its own global scope and executes SHA-256 cryptographic hashing via crypto.subtle.
Starter Code
Line-by-Line Code Breakdown
- Lines 61–76: The worker inspects its global environment. Notice how
typeof windowandtypeof documentevaluate to'undefined', whilefetch,indexedDB, andcrypto.subtleare fully functional. - Lines 78–96: Demonstrates the native Web Crypto API (
crypto.subtle.digest) running directly on a background thread. - Lines 85 (
crypto.subtle.digest('SHA-256', dataBuffer)): Generates a cryptographically secure 256-bit hash off the main thread. - Lines 88–89: Formats the returned
ArrayBufferinto standard lowercase hexadecimal formatting.
Expected Browser Render Output
- Clicking Compute SHA-256 Hash returns:
Hex Digest: d7a8fbb307d7809469ca9ab4cd1192d538abf615b708561c525f25da8593014e
🔍 Web Worker Scope & Web Crypto Engine
Inspect APIs available inside DedicatedWorkerGlobalScope and compute SHA-256 hashes off-thread.
[ Button: 1. Audit Worker Global Scope Capabilities ]
Text to Hash: [ The quick brown fox jumps over the lazy dog ]
[ Button: 2. Compute SHA-256 Hash Off-Thread ]
Scope Diagnostic Report:
{
"globalScopeName": "DedicatedWorkerGlobalScope",
"isWindowDefined": false,
"isDocumentDefined": false,
"isLocalStorageDefined": false,
"hasFetch": true,
"hasIndexedDB": true,
"hasWebSockets": true,
"hasWebCrypto": true,
"hasWebAssembly": true,
"hardwareConcurrency": 16,
"workerUserAgent": "Mozilla/5.0 ... Chrome/120.0"
}🏋️ Hands-On Exercise
🎯 The Challenge: Build a Worker-Driven IndexedDB Log Store
Instructions:
- Create a Web Worker that opens an IndexedDB database named
'WorkerLogsDB'with an object store named'telemetry'. - Provide two worker commands:
{ action: 'INSERT_LOG', message: 'User logged in', level: 'INFO' }: Inserts a log entry with an auto-incrementing key and timestamp.{ action: 'GET_ALL_LOGS' }: Reads all logs from IndexedDB and returns them to the main thread.
- Verify that all IndexedDB transactions occur completely off the main thread inside the worker scope.
🏁 Starter Code Sandbox
⚠️ Common Pitfalls
- Trying to use
localStorage: CallinglocalStorage.getItem()in a worker throwsReferenceError: localStorage is not defined. UseIndexedDBor post messages back to the main thread. - Mixing up
importScripts()and ESimport:importScripts()only works in classic workers. If{ type: 'module' }is configured, callingimportScripts()throwsTypeError: Failed to execute 'importScripts' on 'WorkerGlobalScope': Module scripts don't support importScripts(). - Using relative paths with
importScripts(): Relative paths inimportScripts('./utils.js')resolve relative to the worker script's URL, not the HTML document's URL.
💡 Pro Tips
- Offload Crypto Operations: Hashing large files (e.g. calculating SHA-256 for a 2GB file upload) on the main thread will cause severe jank. Always stream the file chunks into a Web Worker and digest with
crypto.subtle. - Combine IndexedDB and Web Workers: Moving your data layer (IndexedDB persistence, schema migrations, complex ORM queries) entirely into a Dedicated Worker keeps your main thread pure and dedicated strictly to 60fps UI rendering.
📌 Key Takeaways
- The worker global scope is
DedicatedWorkerGlobalScope(referenced byselforglobalThis). - DOM interfaces (
document,window,Element) and synchronous storage (localStorage) are completely unavailable in workers. - Modern Web APIs like
fetch,WebSockets,IndexedDB,crypto.subtle, andWebAssemblyare fully available in workers. - Classic workers load external scripts via synchronous
importScripts(). - Module workers (
type: 'module') use native ES6import/exportsyntax. - --