Web Workers
Overview
As of 2025, Web Workers have become essential for building high-performance web applications, especially with the increasing complexity of client-side processing and the prevalence of data-intensive web apps.
Types of Web Workers
- Dedicated Workers: The most common type that belongs to a single script.
- Shared Workers: Can be accessed by multiple scripts across different windows, iframes, or workers.
- Module Workers: Support ES modules, allowing for better code organization.
Web Workers vs. Service Workers
| Web Worker | Service Worker |
|---|---|
| For CPU-intensive tasks | For network interception and offline capabilities |
Created with new Worker() | Registered with navigator.serviceWorker.register() |
| Terminated when page is closed | Persists between page loads |
| Direct parent-script communication | Event-driven with no direct parent script |
| Simple lifecycle | Complex installation and activation lifecycle |
While JavaScript itself is single-threaded, Web Workers provide true parallelism by creating separate threads, unlike asynchronous programming which still runs on the main thread.
Common Use Cases (2025)
- Data processing: Parsing large datasets, complex calculations
- Real-time applications: Processing streams of data without UI freezing
- Image/video processing: Filters, transformations, and encoding
- AI/ML model inference: Running smaller machine learning models client-side
- Web Assembly integration: Running compiled code for performance-critical operations
Implementation Example
// Main thread
const worker = new Worker('processor.js');
// Send data to the worker
worker.postMessage({
task: 'process',
data: largeDataset
});
// Receive processed data from worker
worker.onmessage = function(e) {
console.log('Processing complete:', e.data.result);
};
// Handle errors
worker.onerror = function(error) {
console.error('Worker error:', error.message);
};
// processor.js (Worker file)
self.onmessage = function(e) {
if (e.data.task === 'process') {
// Perform CPU-intensive work
const result = performComplexCalculation(e.data.data);
// Send result back to main thread
self.postMessage({ result });
}
};
Limitations
- No direct DOM access
- Limited access to some browser APIs
- Additional memory overhead for each worker
- Communication overhead for transferring large data (mitigated with
transferable objects)
Performance Considerations
- Use
transferable objectsfor large data to avoid copying - Consider worker pools for managing multiple concurrent tasks
- Monitor memory usage as each worker has its own memory allocation
- Use
SharedArrayBufferfor more efficient memory sharing (where supported)
Browser Support
As of 2025, Web Workers are supported in all modern browsers including mobile browsers, with broad support for advanced features like Module Workers and SharedArrayBuffer (with proper security headers).
Browser Caching
In Memory / Disk Cache
will pull files from either memory cache or disk cache. Since we didn’t close our browser between Cases 1 & 2, the data was still in memory cache.
Browser Cache
The browser inspects the headers of the HTTP response generated by the web server. There are four headers commonly used for caching:
- ETag
- Cache-Control
- public: public means that the resource can be cached by any cache (browser, CDN, etc)
- private: private means that the resource can only be cached by the browser
- no-store: This tells the browser to always request the resource from the server
- no-cache: This tells the browser to cache the file but not to use it until it checks with the server to validate we have the latest version. This validation is done with the ETag header.
- Expires
- Last-Modified
| Terms | Definitions |
|---|---|
| Javascript Engine | translate JavaScript to binary |
Global Scope vs Lexical Scope
In JavaScript, global scope and lexical scope are two important concepts related to variable accessibility and visibility.
Global Scope: The global scope refers to the outermost scope in JavaScript, which is accessible from anywhere in the code. When you declare a variable outside of any function or block, it becomes a global variable and is added to the global scope. Global variables can be accessed and modified from any part of your code, including within functions and blocks. For example:
var globalVariable = 10;
function myFunction() {
console.log(globalVariable); // Accessible inside the function
}
console.log(globalVariable); // Accessible outside the function
Lexical Scope: Lexical scope, also known as static scope, refers to the scope determined by the position of variables and blocks in the source code. In JavaScript, lexical scoping means that the accessibility of variables is defined by their location at the time of authoring the code. When you declare a variable inside a function or block, it becomes a local variable and is accessible only within that particular function or block. For example:
function myFunction() {
var localVariable = 20;
console.log(localVariable); // Accessible inside the function
}
console.log(localVariable); // Error: localVariable is not defined
Lexical scoping allows inner scopes to access variables from their parent scopes, but not vice versa. So, variables declared in an outer scope are accessible in the inner scope, but variables declared in an inner scope are not accessible in the outer scope.
It's important to note that the introduction of let and const keywords in ES6 introduced block scope, which means variables declared with let and const are limited to the block in which they are defined (e.g., inside an if statement or a for loop). This is different from the function scope created by variables declared with var.
Understanding the concepts of global scope and lexical scope is crucial for writing maintainable and bug-free JavaScript code.
Lexical Scope / Lexical Environment
Every time the JavaScript engine creates an execution context to execute the function or global code, it also creates a new lexical environment to store the variable defined in that function during the execution of that function.
A lexical environment is a data structure that holds identifier-variable mapping. (here identifier refers to the name of variables/functions, and the variable is the reference to actual object [including function type object] or primitive value).
A Lexical Environment has two components: (1) the environment record and (2) a reference to the outer environment.
- The environment record is the actual place where the variable and function declarations are stored.
- The reference to the outer environment means it has access to its outer (parent) lexical environment. This component is the most important in order to understand how closures work.
Execution Context
An execution context is an abstract environment where the JavaScript code is evaluated and executed. When the global code is executed, it’s executed inside the global execution context, and the function code is executed inside the function execution context.
There can only be one currently running execution context (Because JavaScript is single threaded language), which is managed by a stack data structure known as Execution Stack or Call Stack.
An execution stack is a stack with LIFO (Last in, first out) structure in which items can only be added or removed from the top of the stack only.
The currently running execution context will be always on the top of the stack, and when the function which is currently running completes, its execution context is popped off from the stack and the control reaches to the execution context below it in the stack.
Scope is the set of rules that determines where and how a variable (identifier) can be looked-up. This look-up may be for the purposes of assigning to the variable, which is an LHS (left-hand-side) reference, or it may be for the purposes of retrieving its value, which is an RHS (right-hand-side) reference.
LHS references result from assignment operations. Scope-related assignments can occur either with the = operator or by passing arguments to (assign to) function parameters.