LEARNING OBJECTIVES ⌵
- Instantiate dedicated web workers using
new Worker(scriptURL, options). - Configure worker options including
{ type: 'module' },{ credentials: '...' }, and{ name: '...' }. - Understand the Same-Origin Policy (SOP) constraints governing worker script URLs and why
file://execution fails. - Trace the complete lifecycle of a worker from spawning and script execution to idle state and termination.
- Differentiate between external shutdown (
worker.terminate()) and internal self-destruction (self.close()), ensuring prompt garbage collection.
📖 The Mental Model & Story (Intuitive Foundation)
Imagine an engineering manager hiring a remote freelance contractor for a specific high-intensity data analysis project.
THE CONTRACTOR LIFECYCLE ANALOGY
Manager (Main Thread) Contractor (Dedicated Worker)
--------------------- -----------------------------
1. Hires contractor:
`new Worker('contractor.js')` ===========> Contractor spawns in private office
(Dedicated OS Thread initialized)
2. Issues Task:
`worker.postMessage({ job: 1 })` =========> Begins crunching calculations
3. Mid-job Cancellation:
`worker.terminate()` ===========> Contractor instantly evicted!
(Thread killed, RAM cleared instantly)
- OR -
4. Contractor finishes all work:
Contractor cleans desk & logs out:
`self.close()` <=========== Self-terminates when idle
A Dedicated Worker is linked strictly to the single browsing context (web page or script) that created it. It does not share state with other tabs or windows.
If the user closes the browser tab, the dedicated worker is killed immediately. Furthermore, the main thread holds the "kill switch": calling worker.terminate() instantly aborts whatever the worker is doing, freeing up system resources without waiting for the worker loop to yield.
Technical Deep Dive & Specifications
The Worker() Constructor Syntax & Options
The WHATWG specification defines the Worker constructor as follows:
const worker = new Worker(scriptURL, options);
Constructor Parameters:
scriptURL(stringorURL): A valid URL string representing the JavaScript file to execute. It must conform to the Same-Origin Policy (SOP).options(WorkerOptionsobject, optional):
| Property | Type | Default | Description |
|---|---|---|---|
type |
'classic' | 'module' |
'classic' |
Set to 'module' to enable ES6 import/export syntax inside the worker script. |
credentials |
'omit' | 'same-origin' | 'include' |
'same-origin' |
Specifies how HTTP credentials (cookies, auth headers) are sent when fetching the worker script. |
name |
string |
"" |
An optional name for the worker scope, visible in Chrome DevTools under the Sources > Threads inspector. |
// Example: Modern ES Module Worker with descriptive name
const worker = new Worker('./workers/compute.js', {
type: 'module',
name: 'CryptoMatrixWorker'
});
Same-Origin Policy (SOP) Constraints
The worker script URL must share the same origin (protocol, domain, and port) as the parent page.
Origin Check: https://example.com/app/index.html
-> https://example.com/app/worker.js [ALLOWED - Same Origin]
-> https://cdn.example.com/worker.js [BLOCKED - Cross Origin Subdomain]
-> http://example.com/worker.js [BLOCKED - Protocol Mismatch (HTTP vs HTTPS)]
-> file:///C:/project/worker.js [BLOCKED - Origin is 'null' in file://]
[!NOTE] If you need to load a worker from a remote CDN, you can fetch the script content via CORS
fetch()or useimportScripts()inside a local shim, or wrap the script in an inline Blob URL (covered in Lesson 50.9).
The Worker Lifecycle & State Machine
A Dedicated Worker passes through distinct lifecycle states:
+-----------------------------------------------------------------------------------+
| WORKER LIFECYCLE PIPELINE |
+-----------------------------------------------------------------------------------+
[ 1. Instantiation ] ----> Main thread calls `new Worker('worker.js')`
|
v
[ 2. Script Fetch ] -----> Browser fetches script (SOP verified)
|
v
[ 3. Thread Spawn ] -----> Dedicated OS thread allocated, V8 isolate initialized
|
v
[ 4. Execution ] ---------> Top-level script executes synchronously
|
v
[ 5. Event Loop Idle ] ---> Worker enters event loop waiting for `message` events
|
+--------------> Processing messages via `onmessage` handlers
|
+--------------> Periodic tasks / timers (`setInterval`)
|
v
[ 6. Termination ]
|
+---> From Main Thread: `worker.terminate()` (Abrupt stop, sync teardown)
|
+---> From Inside Worker: `self.close()` (Finishes current macro-task, dies)
|
v
[ 7. Garbage Collection ] Memory heap reclaimed; thread destroyed by OS.
worker.terminate() vs self.close()
| Feature | worker.terminate() |
self.close() |
|---|---|---|
| Called From | Main UI Thread | Inside Worker Thread (self scope) |
| Execution Stopping | Immediate & Abrupt. Drops pending micro/macrotasks. | Finishes the currently executing event loop task, then shuts down. |
| Cleanup Hooks | Does not fire onclose or beforeunload. |
Allows local script to finish current synchronous block. |
| Garbage Collection | Marks the worker instance for immediate GC. | Marks the worker instance for GC after thread exit. |
💻 Interactive Code Playground
Here is a full, interactive implementation of an Interruptible Prime Number Generator. You can start the calculation, observe real-time batch progress, and terminate the worker mid-stream.
Starter Code
Line-by-Line Code Breakdown
- Lines 61–74: The worker defines a highly optimized
isPrime()algorithm checking $6k \pm 1$ divisibility. - Lines 76–94: The worker listens for
{ action: 'FIND_PRIMES' }. Instead of waiting for all 10 million iterations to finish, it periodically posts{ type: 'PROGRESS' }every 20,000 primes. - Line 92 (
self.close()): Once all primes are computed, the worker autonomously terminates itself, freeing memory without requiring the main thread to manage it. - Line 101 (
new Worker(workerUrl, { name: 'PrimeHunterWorker' })): Spawns the dedicated worker with a custom thread name visible in DevTools. - Lines 135–144 (
worker.terminate()): When the user clicks the red "Kill Switch" button,worker.terminate()immediately destroys the background thread mid-execution.
Expected Browser Render Output
- Clicking Terminate Worker instantly halts prime generation at whatever count was reached and logs
worker.terminate() called!.
⚙️ Dedicated Worker Lifecycle Manager
Spawn, communicate with, and terminate an OS worker calculating prime numbers up to 10,000,000.
[Panel]
Worker Thread Status: Not Spawned (Dead)
Primes Found: 0
[ Button: 1. Spawn & Start Worker ] [ Button: 2. Terminate Worker (Kill Switch) ] [ Button: Clear Log ]
Lifecycle & Progress Log:
[02:20:01] Worker spawned: new Worker("PrimeHunterWorker")
[02:20:01] Message posted: { action: "FIND_PRIMES", limit: 10,000,000 }
[02:20:02] Progress: Found 20,000 primes (reached 224,737)
[02:20:03] Progress: Found 40,000 primes (reached 479,909)🏋️ Hands-On Exercise
🎯 The Challenge: Implement a Worker Watchdog with Automatic Timeout
Instructions:
- Build a helper function
runWorkerWithTimeout(taskData, timeoutMs). - The function spawns a dedicated worker to process
taskData. - If the worker completes before
timeoutMs, resolve a Promise with the result and terminate the worker. - If the worker exceeds
timeoutMs, forcefully callworker.terminate(), reject the Promise withnew Error('Worker timed out'), and ensure no memory leaks occur.
🏁 Starter Code Sandbox
⚠️ Common Pitfalls
- Testing over
file://protocol: Launching an HTML file directly via double-click (file:///...) triggers CORS/SOP security errors when spawning workers (DOMException: Failed to construct 'Worker'). Always use a local web server (e.g.,npx serve, VS Code Live Server). - Forgetting to Revoke Blob URLs: When creating inline workers using
URL.createObjectURL(blob), failing to callURL.revokeObjectURL(url)leaves the blob in memory for the lifetime of the document. - Continuing to Post Messages After
worker.terminate(): Callingworker.postMessage()on an already-terminated worker throws no error, but messages will be silently discarded.
💡 Pro Tips
- Name Your Workers for DevTools Debugging: Pass
{ name: 'WorkerName' }in the options object. In Chrome DevTools, open the Sources tab, expand Threads in the right pane, and you will see your worker distinctly labeled instead of an anonymousworker.js. - Leverage
self.close()for Ephemeral Workers: If a worker is spawned for a one-off computation, have it callself.close()as its final statement. This guarantees automatic thread cleanup without requiring the main thread to track active worker instances.
📌 Key Takeaways
- Dedicated Workers are created via
new Worker(scriptURL, { type, credentials, name }). - Worker script URLs are strictly constrained by the Same-Origin Policy.
worker.terminate()halts the worker immediately from the main thread.self.close()allows a worker to cleanly terminate itself from inside its own scope.- Once terminated, the worker's thread and memory isolate are scheduled for immediate garbage collection.
- --