Dev Logs
/JavaScript/ Chapter 21: JavaScript Threading Model & Web Workers
Chapters
  • 01Chapter 1: Variables & Data Types
  • 02Chapter 2: Operators & Expressions
  • 03Chapter 3: Control Flow
  • 04Chapter 4: Functions Deep Dive
  • 05Chapter 5: Scope, Hoisting & TDZ
  • 06Chapter 6: Closures Explained
  • 07Chapter 7: Arrays & Array Methods
  • 08Chapter 8: Objects & Object Access
  • 09Chapter 9: Prototypes & Inheritance
  • 10Chapter 10: Asynchronous JavaScript
  • 11Chapter 11: Error Handling & Debugging
  • 12Chapter 12: ES6+ Features
  • 13Chapter 13: DOM Manipulation
  • 14Chapter 14: Event Handling
  • 15. Browser APIs
  • 16Chapter 16: Testing & Debugging πŸ§ͺ
  • 17Chapter 17: Performance Optimization ⚑
  • 18Chapter 18: Security Best Practices πŸ”’
  • 19Chapter 19: Modern JavaScript Frameworks πŸš€
  • 20Chapter 20: Node.js & Backend Development πŸš€
  • 21Chapter 21: JavaScript Threading Model & Web Workers
    • Plain English Explanation
    • JavaScript's Single-Threaded Nature
    • Why Single-Threaded?
    • The Event Loop in Action
    • Problems with Single Threading
    • Web Workers: JavaScript's Multi-Threading Solution
    • Basic Web Worker Setup
    • Handling Billion Data Points with Workers
    • Chunked Processing Strategy
    • Advanced Worker for Complex Calculations
    • Advanced Worker Patterns
    • Worker Pool Manager
    • Transferable Objects for Performance
    • Using ArrayBuffers for Large Data
    • Performance Comparison
    • Benchmarking Different Approaches
    • Best Practices for Complex Calculations
    • . Choose the Right Approach
    • . Memory Management
    • . Progress Reporting
    • Common Interview Questions
    • Key Takeaways
All chapters

πŸ“š Chapter 21: JavaScript Threading Model & Web Workers

Understanding JavaScript's single-threaded nature and how to handle complex computations with Web Workers.

πŸ“– Plain English Explanation

Imagine JavaScript as a single chef in a kitchen:

  • Single-threaded = One chef doing all tasks one at a time
  • Event Loop = The chef's to-do list that keeps getting updated
  • Web Workers = Hiring additional chefs to work in separate kitchens
  • Complex Calculations = Heavy prep work that can be delegated to other chefs

JavaScript runs on a single main thread, but it can delegate heavy work to Web Workers (background threads) to prevent blocking the user interface.

🧡 JavaScript's Single-Threaded Nature

Why Single-Threaded?

javascript
// JavaScript was designed for simple web interactions
// Single-threaded model prevents many concurrency issues

// ❌ This would block everything for 5 seconds
function heavyCalculation() {
    const start = Date.now();
    let result = 0;
    
    // Simulate heavy computation
    while (Date.now() - start < 5000) {
        result += Math.random();
    }
    
    return result;
}

console.log('Start');
const result = heavyCalculation(); // Blocks UI for 5 seconds!
console.log('Result:', result);
console.log('End'); // Only runs after 5 seconds

The Event Loop in Action

javascript
// Understanding execution order
console.log('1: Synchronous code');

// Macrotask (Timer)
setTimeout(() => {
    console.log('4: setTimeout (macrotask)');
}, 0);

// Microtask (Promise)
Promise.resolve().then(() => {
    console.log('3: Promise (microtask)');
});

console.log('2: More synchronous code');

// Output:
// 1: Synchronous code
// 2: More synchronous code
// 3: Promise (microtask)
// 4: setTimeout (macrotask)

/*
Execution Order:
1. Call Stack (synchronous code)
2. Microtask Queue (Promises, queueMicrotask)
3. Macrotask Queue (setTimeout, setInterval, I/O)
*/

Problems with Single Threading

javascript
// ❌ Processing large datasets blocks the UI
function processLargeDataset(data) {
    const results = [];
    
    // This could take seconds with millions of items
    for (let i = 0; i < data.length; i++) {
        // Complex calculation
        results.push(data[i] * Math.sqrt(data[i]) + Math.sin(data[i]));
    }
    
    return results;
}

// Simulate billion data points
const billionNumbers = new Array(1000000).fill(0).map(() => Math.random());

// This will freeze the browser!
// const processed = processLargeDataset(billionNumbers);

πŸ‘· Web Workers: JavaScript's Multi-Threading Solution

Basic Web Worker Setup

javascript
// main.js - Main thread
if (typeof Worker !== 'undefined') {
    // Create a new worker
    const worker = new Worker('worker.js');
    
    // Send data to worker
    worker.postMessage({
        command: 'process',
        data: [1, 2, 3, 4, 5]
    });
    
    // Receive results from worker
    worker.onmessage = function(e) {
        console.log('Result from worker:', e.data);
        
        // Clean up when done
        worker.terminate();
    };
    
    // Handle errors
    worker.onerror = function(error) {
        console.error('Worker error:', error);
    };
} else {
    console.log('Web Workers not supported');
}
javascript
// worker.js - Worker thread
self.onmessage = function(e) {
    const { command, data } = e.data;
    
    if (command === 'process') {
        // Heavy computation runs in background
        const result = processData(data);
        
        // Send result back to main thread
        self.postMessage({
            success: true,
            result: result,
            processingTime: Date.now() - startTime
        });
    }
};

function processData(data) {
    const startTime = Date.now();
    const results = [];
    
    // Complex calculations don't block main thread
    for (let i = 0; i < data.length; i++) {
        results.push(data[i] * Math.sqrt(data[i]) + Math.sin(data[i]));
    }
    
    return results;
}

// Handle errors in worker
self.onerror = function(error) {
    self.postMessage({
        success: false,
        error: error.message
    });
};

πŸ”’ Handling Billion Data Points with Workers

Chunked Processing Strategy

javascript
// main.js - Processing large datasets efficiently
class BigDataProcessor {
    constructor(workerScript, chunkSize = 100000) {
        this.workerScript = workerScript;
        this.chunkSize = chunkSize;
        this.workers = [];
        this.maxWorkers = navigator.hardwareConcurrency || 4;
    }
    
    async processBillionDataPoints(data) {
        console.log(`Processing ${data.length} data points...`);
        const startTime = Date.now();
        
        // Split data into chunks
        const chunks = this.chunkArray(data, this.chunkSize);
        console.log(`Split into ${chunks.length} chunks`);
        
        // Process chunks in parallel using worker pool
        const results = await this.processChunksInParallel(chunks);
        
        // Combine results
        const finalResult = results.flat();
        
        const processingTime = Date.now() - startTime;
        console.log(`Processed ${finalResult.length} items in ${processingTime}ms`);
        
        return finalResult;
    }
    
    chunkArray(array, chunkSize) {
        const chunks = [];
        for (let i = 0; i < array.length; i += chunkSize) {
            chunks.push(array.slice(i, i + chunkSize));
        }
        return chunks;
    }
    
    async processChunksInParallel(chunks) {
        const results = [];
        const workerPromises = [];
        
        // Process chunks using worker pool
        for (let i = 0; i < chunks.length; i++) {
            const workerIndex = i % this.maxWorkers;
            
            const promise = this.processChunkWithWorker(chunks[i], workerIndex);
            workerPromises.push(promise);
        }
        
        // Wait for all workers to complete
        const allResults = await Promise.all(workerPromises);
        
        // Clean up workers
        this.terminateAllWorkers();
        
        return allResults;
    }
    
    processChunkWithWorker(chunk, workerIndex) {
        return new Promise((resolve, reject) => {
            // Create worker if doesn't exist
            if (!this.workers[workerIndex]) {
                this.workers[workerIndex] = new Worker(this.workerScript);
            }
            
            const worker = this.workers[workerIndex];
            
            // Set up message handler
            const messageHandler = (e) => {
                worker.removeEventListener('message', messageHandler);
                
                if (e.data.success) {
                    resolve(e.data.result);
                } else {
                    reject(new Error(e.data.error));
                }
            };
            
            worker.addEventListener('message', messageHandler);
            
            // Send chunk to worker
            worker.postMessage({
                command: 'processChunk',
                data: chunk,
                chunkIndex: workerIndex
            });
        });
    }
    
    terminateAllWorkers() {
        this.workers.forEach(worker => {
            if (worker) {
                worker.terminate();
            }
        });
        this.workers = [];
    }
}

// Usage example
async function demonstrateBigDataProcessing() {
    // Generate billion data points (adjust size for testing)
    const dataSize = 1000000; // 1 million for demo
    const bigData = new Array(dataSize).fill(0).map(() => Math.random() * 1000);
    
    console.log('Generated data, starting processing...');
    
    const processor = new BigDataProcessor('complex-worker.js', 50000);
    
    try {
        const results = await processor.processBillionDataPoints(bigData);
        console.log('Processing completed!', {
            originalSize: bigData.length,
            resultSize: results.length,
            sampleResults: results.slice(0, 5)
        });
    } catch (error) {
        console.error('Processing failed:', error);
    }
}

// Start processing
// demonstrateBigDataProcessing();

Advanced Worker for Complex Calculations

javascript
// complex-worker.js - Advanced worker for heavy computations
class ComplexCalculationWorker {
    constructor() {
        this.setupMessageHandler();
    }
    
    setupMessageHandler() {
        self.onmessage = (e) => {
            const { command, data, chunkIndex } = e.data;
            
            try {
                switch (command) {
                    case 'processChunk':
                        this.processChunk(data, chunkIndex);
                        break;
                    case 'complexMath':
                        this.performComplexMath(data);
                        break;
                    case 'dataAnalysis':
                        this.performDataAnalysis(data);
                        break;
                    default:
                        throw new Error(`Unknown command: ${command}`);
                }
            } catch (error) {
                self.postMessage({
                    success: false,
                    error: error.message,
                    chunkIndex
                });
            }
        };
    }
    
    processChunk(data, chunkIndex) {
        const startTime = Date.now();
        const results = [];
        
        // Complex mathematical operations
        for (let i = 0; i < data.length; i++) {
            const value = data[i];
            
            // Simulate complex calculation
            const result = this.complexCalculation(value);
            results.push(result);
            
            // Report progress for large chunks
            if (i % 10000 === 0 && i > 0) {
                self.postMessage({
                    type: 'progress',
                    chunkIndex,
                    processed: i,
                    total: data.length,
                    percentage: (i / data.length * 100).toFixed(2)
                });
            }
        }
        
        const processingTime = Date.now() - startTime;
        
        self.postMessage({
            success: true,
            result: results,
            chunkIndex,
            processingTime,
            itemsProcessed: data.length
        });
    }
    
    complexCalculation(value) {
        // Simulate CPU-intensive calculation
        let result = value;
        
        // Mathematical transformations
        result = Math.sqrt(result * Math.PI);
        result = Math.sin(result) * Math.cos(result);
        result = Math.pow(result, 2);
        result = Math.log(Math.abs(result) + 1);
        
        // Statistical operations
        result = this.normalizeValue(result);
        
        return result;
    }
    
    normalizeValue(value) {
        // Normalize to 0-1 range
        return (value - Math.floor(value));
    }
    
    performComplexMath(data) {
        const results = {
            sum: 0,
            mean: 0,
            variance: 0,
            standardDeviation: 0,
            min: Infinity,
            max: -Infinity
        };
        
        // Calculate basic statistics
        for (let value of data) {
            results.sum += value;
            results.min = Math.min(results.min, value);
            results.max = Math.max(results.max, value);
        }
        
        results.mean = results.sum / data.length;
        
        // Calculate variance
        let varianceSum = 0;
        for (let value of data) {
            varianceSum += Math.pow(value - results.mean, 2);
        }
        
        results.variance = varianceSum / data.length;
        results.standardDeviation = Math.sqrt(results.variance);
        
        self.postMessage({
            success: true,
            result: results
        });
    }
    
    performDataAnalysis(data) {
        const analysis = {
            histogram: this.createHistogram(data),
            quartiles: this.calculateQuartiles(data),
            outliers: this.detectOutliers(data)
        };
        
        self.postMessage({
            success: true,
            result: analysis
        });
    }
    
    createHistogram(data, bins = 10) {
        const min = Math.min(...data);
        const max = Math.max(...data);
        const binSize = (max - min) / bins;
        const histogram = new Array(bins).fill(0);
        
        for (let value of data) {
            const binIndex = Math.min(Math.floor((value - min) / binSize), bins - 1);
            histogram[binIndex]++;
        }
        
        return histogram;
    }
    
    calculateQuartiles(data) {
        const sorted = [...data].sort((a, b) => a - b);
        const n = sorted.length;
        
        return {
            q1: sorted[Math.floor(n * 0.25)],
            q2: sorted[Math.floor(n * 0.5)], // median
            q3: sorted[Math.floor(n * 0.75)]
        };
    }
    
    detectOutliers(data) {
        const quartiles = this.calculateQuartiles(data);
        const iqr = quartiles.q3 - quartiles.q1;
        const lowerBound = quartiles.q1 - 1.5 * iqr;
        const upperBound = quartiles.q3 + 1.5 * iqr;
        
        return data.filter(value => value < lowerBound || value > upperBound);
    }
}

// Initialize worker
const worker = new ComplexCalculationWorker();

πŸš€ Advanced Worker Patterns

Worker Pool Manager

javascript
// worker-pool.js - Efficient worker pool management
class WorkerPool {
    constructor(workerScript, poolSize = navigator.hardwareConcurrency || 4) {
        this.workerScript = workerScript;
        this.poolSize = poolSize;
        this.workers = [];
        this.taskQueue = [];
        this.activeJobs = new Map();
        
        this.initializeWorkers();
    }
    
    initializeWorkers() {
        for (let i = 0; i < this.poolSize; i++) {
            const worker = new Worker(this.workerScript);
            worker.id = i;
            worker.busy = false;
            
            worker.onmessage = (e) => {
                this.handleWorkerMessage(worker, e);
            };
            
            worker.onerror = (error) => {
                this.handleWorkerError(worker, error);
            };
            
            this.workers.push(worker);
        }
    }
    
    execute(data, transferable = []) {
        return new Promise((resolve, reject) => {
            const job = {
                data,
                transferable,
                resolve,
                reject,
                id: Date.now() + Math.random()
            };
            
            const availableWorker = this.workers.find(w => !w.busy);
            
            if (availableWorker) {
                this.assignJob(availableWorker, job);
            } else {
                this.taskQueue.push(job);
            }
        });
    }
    
    assignJob(worker, job) {
        worker.busy = true;
        this.activeJobs.set(worker.id, job);
        worker.postMessage(job.data, job.transferable);
    }
    
    handleWorkerMessage(worker, e) {
        const job = this.activeJobs.get(worker.id);
        
        if (job) {
            job.resolve(e.data);
            this.activeJobs.delete(worker.id);
            worker.busy = false;
            
            // Process next job in queue
            if (this.taskQueue.length > 0) {
                const nextJob = this.taskQueue.shift();
                this.assignJob(worker, nextJob);
            }
        }
    }
    
    handleWorkerError(worker, error) {
        const job = this.activeJobs.get(worker.id);
        
        if (job) {
            job.reject(error);
            this.activeJobs.delete(worker.id);
            worker.busy = false;
        }
    }
    
    terminate() {
        this.workers.forEach(worker => worker.terminate());
        this.workers = [];
        this.taskQueue = [];
        this.activeJobs.clear();
    }
    
    getStats() {
        return {
            totalWorkers: this.workers.length,
            busyWorkers: this.workers.filter(w => w.busy).length,
            queuedJobs: this.taskQueue.length,
            activeJobs: this.activeJobs.size
        };
    }
}

// Usage example
async function demonstrateWorkerPool() {
    const workerPool = new WorkerPool('complex-worker.js', 4);
    
    // Create multiple large tasks
    const tasks = [];
    for (let i = 0; i < 10; i++) {
        const data = new Array(100000).fill(0).map(() => Math.random());
        tasks.push(data);
    }
    
    console.log('Starting parallel processing...');
    const startTime = Date.now();
    
    try {
        // Process all tasks in parallel
        const results = await Promise.all(
            tasks.map(task => workerPool.execute({
                command: 'processChunk',
                data: task
            }))
        );
        
        const processingTime = Date.now() - startTime;
        console.log(`Processed ${tasks.length} tasks in ${processingTime}ms`);
        console.log('Worker stats:', workerPool.getStats());
        
    } catch (error) {
        console.error('Processing failed:', error);
    } finally {
        workerPool.terminate();
    }
}

πŸ”„ Transferable Objects for Performance

Using ArrayBuffers for Large Data

javascript
// main.js - Efficient data transfer
function processLargeArrayWithTransfer() {
    // Create large array buffer (simulating billion data points)
    const size = 1000000; // 1 million numbers
    const buffer = new ArrayBuffer(size * 4); // 4 bytes per float32
    const data = new Float32Array(buffer);
    
    // Fill with random data
    for (let i = 0; i < size; i++) {
        data[i] = Math.random() * 1000;
    }
    
    console.log('Original buffer size:', buffer.byteLength, 'bytes');
    
    const worker = new Worker('transfer-worker.js');
    
    worker.onmessage = (e) => {
        const { result, processingTime } = e.data;
        console.log(`Processing completed in ${processingTime}ms`);
        console.log('Result buffer size:', result.byteLength, 'bytes');
        
        // Convert back to typed array
        const resultArray = new Float32Array(result);
        console.log('Sample results:', resultArray.slice(0, 5));
        
        worker.terminate();
    };
    
    // Transfer ownership of buffer to worker (zero-copy)
    worker.postMessage({
        command: 'processLargeArray',
        buffer: buffer
    }, [buffer]); // Transfer list
    
    // buffer is now neutered (unusable) in main thread
    console.log('Buffer transferred, main thread buffer size:', buffer.byteLength);
}

// transfer-worker.js
self.onmessage = function(e) {
    const { command, buffer } = e.data;
    
    if (command === 'processLargeArray') {
        const startTime = Date.now();
        
        // Work directly with transferred buffer
        const data = new Float32Array(buffer);
        
        // Process data in-place
        for (let i = 0; i < data.length; i++) {
            data[i] = Math.sqrt(data[i]) * Math.sin(data[i]);
        }
        
        const processingTime = Date.now() - startTime;
        
        // Transfer buffer back to main thread
        self.postMessage({
            result: buffer,
            processingTime: processingTime
        }, [buffer]);
    }
};

πŸ“Š Performance Comparison

Benchmarking Different Approaches

javascript
// performance-test.js
class PerformanceTester {
    static async compareProcessingMethods(dataSize = 1000000) {
        const testData = new Array(dataSize).fill(0).map(() => Math.random() * 1000);
        
        console.log(`Testing with ${dataSize} data points...\n`);
        
        // Test 1: Synchronous processing (blocks main thread)
        console.log('1. Synchronous Processing:');
        const syncStart = Date.now();
        const syncResult = this.processSynchronously(testData);
        const syncTime = Date.now() - syncStart;
        console.log(`   Time: ${syncTime}ms (BLOCKS UI)\n`);
        
        // Test 2: Chunked processing with setTimeout
        console.log('2. Chunked Processing (setTimeout):');
        const chunkStart = Date.now();
        const chunkResult = await this.processInChunks(testData);
        const chunkTime = Date.now() - chunkStart;
        console.log(`   Time: ${chunkTime}ms (Non-blocking)\n`);
        
        // Test 3: Web Worker processing
        console.log('3. Web Worker Processing:');
        const workerStart = Date.now();
        const workerResult = await this.processWithWorker(testData);
        const workerTime = Date.now() - workerStart;
        console.log(`   Time: ${workerTime}ms (Non-blocking)\n`);
        
        // Test 4: Multiple workers
        console.log('4. Multiple Workers:');
        const multiStart = Date.now();
        const multiResult = await this.processWithMultipleWorkers(testData);
        const multiTime = Date.now() - multiStart;
        console.log(`   Time: ${multiTime}ms (Non-blocking)\n`);
        
        // Summary
        console.log('Performance Summary:');
        console.log(`Synchronous:     ${syncTime}ms (baseline)`);
        console.log(`Chunked:         ${chunkTime}ms (${(chunkTime/syncTime*100).toFixed(1)}% of sync)`);
        console.log(`Single Worker:   ${workerTime}ms (${(workerTime/syncTime*100).toFixed(1)}% of sync)`);
        console.log(`Multiple Workers: ${multiTime}ms (${(multiTime/syncTime*100).toFixed(1)}% of sync)`);
    }
    
    static processSynchronously(data) {
        return data.map(value => Math.sqrt(value) * Math.sin(value));
    }
    
    static processInChunks(data, chunkSize = 10000) {
        return new Promise((resolve) => {
            const result = [];
            let index = 0;
            
            function processChunk() {
                const endIndex = Math.min(index + chunkSize, data.length);
                
                for (let i = index; i < endIndex; i++) {
                    result.push(Math.sqrt(data[i]) * Math.sin(data[i]));
                }
                
                index = endIndex;
                
                if (index < data.length) {
                    setTimeout(processChunk, 0); // Yield to event loop
                } else {
                    resolve(result);
                }
            }
            
            processChunk();
        });
    }
    
    static processWithWorker(data) {
        return new Promise((resolve, reject) => {
            const worker = new Worker('performance-worker.js');
            
            worker.onmessage = (e) => {
                resolve(e.data.result);
                worker.terminate();
            };
            
            worker.onerror = reject;
            
            worker.postMessage({ data });
        });
    }
    
    static async processWithMultipleWorkers(data) {
        const workerPool = new WorkerPool('performance-worker.js', 4);
        const chunkSize = Math.ceil(data.length / 4);
        const chunks = [];
        
        for (let i = 0; i < data.length; i += chunkSize) {
            chunks.push(data.slice(i, i + chunkSize));
        }
        
        try {
            const results = await Promise.all(
                chunks.map(chunk => workerPool.execute({ data: chunk }))
            );
            
            return results.flat();
        } finally {
            workerPool.terminate();
        }
    }
}

// Run performance test
// PerformanceTester.compareProcessingMethods(1000000);

🎯 Best Practices for Complex Calculations

1. Choose the Right Approach

javascript
// Decision matrix for processing strategies
function chooseProcessingStrategy(dataSize, complexity, uiBlocking) {
    if (dataSize < 10000 && complexity === 'low') {
        return 'synchronous'; // Fast enough for main thread
    }
    
    if (dataSize < 100000 && uiBlocking === 'acceptable') {
        return 'chunked'; // Break into smaller pieces
    }
    
    if (dataSize < 1000000) {
        return 'single-worker'; // Use one worker
    }
    
    return 'worker-pool'; // Use multiple workers
}

2. Memory Management

javascript
// Efficient memory usage with workers
class MemoryEfficientProcessor {
    static async processLargeDataset(data) {
        // Use transferable objects to avoid copying
        const buffer = new ArrayBuffer(data.length * 8);
        const view = new Float64Array(buffer);
        
        // Copy data to buffer
        for (let i = 0; i < data.length; i++) {
            view[i] = data[i];
        }
        
        const worker = new Worker('memory-worker.js');
        
        return new Promise((resolve, reject) => {
            worker.onmessage = (e) => {
                const result = new Float64Array(e.data.buffer);
                resolve(Array.from(result));
                worker.terminate();
            };
            
            worker.onerror = reject;
            
            // Transfer buffer ownership
            worker.postMessage({ buffer }, [buffer]);
        });
    }
}

3. Progress Reporting

javascript
// Worker with progress reporting
// progress-worker.js
self.onmessage = function(e) {
    const { data } = e.data;
    const results = [];
    const total = data.length;
    
    for (let i = 0; i < total; i++) {
        // Process item
        results.push(Math.sqrt(data[i]) * Math.sin(data[i]));
        
        // Report progress every 1%
        if (i % Math.floor(total / 100) === 0) {
            self.postMessage({
                type: 'progress',
                completed: i,
                total: total,
                percentage: (i / total * 100).toFixed(1)
            });
        }
    }
    
    self.postMessage({
        type: 'complete',
        result: results
    });
};

πŸ€” Common Interview Questions

Q: Why is JavaScript single-threaded? A: JavaScript was designed for simple web interactions. Single-threading prevents race conditions and makes the language easier to reason about. The event loop handles asynchronous operations without true multithreading.

Q: How do you handle CPU-intensive tasks in JavaScript? A: Use Web Workers to move heavy computations off the main thread, implement chunked processing with setTimeout, or use requestIdleCallback for non-critical tasks.

Q: What's the difference between Web Workers and Service Workers? A: Web Workers are for parallel processing and don't persist between sessions. Service Workers act as network proxies, handle caching, and persist between sessions.

Q: How do you process a billion data points efficiently? A: Split data into chunks, use a worker pool for parallel processing, leverage transferable objects for zero-copy data transfer, and implement progress reporting.

Q: What are transferable objects? A: Objects that can be transferred between threads without copying (zero-copy transfer). Includes ArrayBuffers, MessagePorts, and ImageBitmaps.

🎯 Key Takeaways

  1. JavaScript is single-threaded - but can delegate work to Web Workers
  2. Use Web Workers for heavy computations - prevents UI blocking
  3. Implement chunked processing - for better user experience
  4. Leverage transferable objects - for efficient data transfer
  5. Use worker pools - for managing multiple workers efficiently
  6. Always handle errors - workers can fail independently
  7. Clean up resources - terminate workers when done
  8. Consider memory usage - large datasets need careful management

Previous Chapter: ← Node.js Backend Development
Next Chapter: Modern JavaScript Frameworks β†’

Practice: Build a data processing application that can handle millions of data points without blocking the UI!

PreviousChapter 20: Node.js & Backend Development πŸš€

Open source, free forever. Built by iammhador.

Contribute on GitHub