javascript

examples

examples.js
/**
 * Web Workers - Examples
 * Running JavaScript in background threads
 *
 * Note: These examples require a browser environment.
 * Workers cannot be created from inline scripts.
 */

// =============================================================================
// 1. WORKER CREATION (INLINE EXAMPLE)
// =============================================================================

console.log('--- Web Worker Concepts ---');

// In a real browser environment, you would create a worker like this:
/*
const worker = new Worker('worker.js');

worker.postMessage({ type: 'start', data: [1, 2, 3, 4, 5] });

worker.onmessage = (event) => {
    console.log('Received from worker:', event.data);
};

worker.onerror = (error) => {
    console.error('Worker error:', error.message);
};
*/

// =============================================================================
// 2. SIMULATING WORKER BEHAVIOR
// =============================================================================

// For demonstration, we'll simulate worker behavior
class MockWorker {
  constructor(workerFn) {
    this.workerFn = workerFn;
    this.onmessage = null;
    this.onerror = null;
  }

  postMessage(data) {
    // Simulate async worker processing
    setTimeout(() => {
      try {
        const result = this.workerFn(data);
        if (this.onmessage) {
          this.onmessage({ data: result });
        }
      } catch (error) {
        if (this.onerror) {
          this.onerror(error);
        }
      }
    }, 0);
  }

  terminate() {
    this.onmessage = null;
    this.onerror = null;
  }
}

// =============================================================================
// 3. HEAVY COMPUTATION EXAMPLE
// =============================================================================

console.log('\n--- Heavy Computation ---');

// Worker function for prime number calculation
function primeWorkerFn(data) {
  const { start, end } = data;

  function isPrime(n) {
    if (n < 2) return false;
    for (let i = 2; i <= Math.sqrt(n); i++) {
      if (n % i === 0) return false;
    }
    return true;
  }

  const primes = [];
  for (let i = start; i <= end; i++) {
    if (isPrime(i)) {
      primes.push(i);
    }
  }

  return { primes, count: primes.length };
}

const primeWorker = new MockWorker(primeWorkerFn);

primeWorker.onmessage = (event) => {
  console.log(`Found ${event.data.count} primes`);
  console.log('First 10:', event.data.primes.slice(0, 10));
};

primeWorker.postMessage({ start: 1, end: 1000 });

// =============================================================================
// 4. WORKER WITH PROMISE WRAPPER
// =============================================================================

console.log('\n--- Promise-Based Worker ---');

function createPromiseWorker(workerFn) {
  return function (data) {
    return new Promise((resolve, reject) => {
      const worker = new MockWorker(workerFn);

      worker.onmessage = (event) => {
        resolve(event.data);
      };

      worker.onerror = (error) => {
        reject(error);
      };

      worker.postMessage(data);
    });
  };
}

const asyncPrimes = createPromiseWorker(primeWorkerFn);

asyncPrimes({ start: 1, end: 500 })
  .then((result) => console.log('Async result:', result.count, 'primes'))
  .catch((error) => console.error('Error:', error));

// =============================================================================
// 5. WORKER POOL
// =============================================================================

console.log('\n--- Worker Pool ---');

class WorkerPool {
  constructor(workerFn, size = 4) {
    this.workers = [];
    this.queue = [];
    this.size = size;
    this.workerFn = workerFn;

    for (let i = 0; i < size; i++) {
      this.workers.push({
        worker: new MockWorker(workerFn),
        busy: false,
      });
    }
  }

  execute(data) {
    return new Promise((resolve, reject) => {
      this.queue.push({ data, resolve, reject });
      this.processQueue();
    });
  }

  processQueue() {
    const availableWorker = this.workers.find((w) => !w.busy);

    if (!availableWorker || this.queue.length === 0) {
      return;
    }

    const { data, resolve, reject } = this.queue.shift();
    availableWorker.busy = true;

    availableWorker.worker.onmessage = (event) => {
      availableWorker.busy = false;
      resolve(event.data);
      this.processQueue();
    };

    availableWorker.worker.onerror = (error) => {
      availableWorker.busy = false;
      reject(error);
      this.processQueue();
    };

    availableWorker.worker.postMessage(data);
  }

  terminate() {
    this.workers.forEach((w) => w.worker.terminate());
    this.workers = [];
  }
}

// Using the pool
const pool = new WorkerPool(primeWorkerFn, 2);

async function runPoolExample() {
  const ranges = [
    { start: 1, end: 250 },
    { start: 251, end: 500 },
    { start: 501, end: 750 },
    { start: 751, end: 1000 },
  ];

  console.time('Pool execution');
  const results = await Promise.all(ranges.map((range) => pool.execute(range)));
  console.timeEnd('Pool execution');

  const totalPrimes = results.reduce((sum, r) => sum + r.count, 0);
  console.log('Total primes found:', totalPrimes);
}

setTimeout(runPoolExample, 100);

// =============================================================================
// 6. MESSAGE TYPES AND PROTOCOLS
// =============================================================================

console.log('\n--- Message Protocol ---');

// Worker with message types
function messageProtocolWorker(message) {
  const { type, payload, id } = message;

  switch (type) {
    case 'CALCULATE':
      return {
        type: 'CALCULATE_RESULT',
        id,
        payload: payload.reduce((a, b) => a + b, 0),
      };

    case 'TRANSFORM':
      return {
        type: 'TRANSFORM_RESULT',
        id,
        payload: payload.map((x) => x * 2),
      };

    case 'FILTER':
      return {
        type: 'FILTER_RESULT',
        id,
        payload: payload.filter((x) => x > 5),
      };

    default:
      return {
        type: 'ERROR',
        id,
        error: `Unknown message type: ${type}`,
      };
  }
}

// Message handler with request/response tracking
class WorkerRPC {
  constructor(workerFn) {
    this.worker = new MockWorker(workerFn);
    this.pending = new Map();
    this.nextId = 0;

    this.worker.onmessage = (event) => {
      const { id, type, payload, error } = event.data;

      if (this.pending.has(id)) {
        const { resolve, reject } = this.pending.get(id);
        this.pending.delete(id);

        if (type === 'ERROR') {
          reject(new Error(error));
        } else {
          resolve(payload);
        }
      }
    };
  }

  call(type, payload) {
    const id = this.nextId++;

    return new Promise((resolve, reject) => {
      this.pending.set(id, { resolve, reject });
      this.worker.postMessage({ type, payload, id });
    });
  }
}

const rpc = new WorkerRPC(messageProtocolWorker);

setTimeout(async () => {
  console.log('\n--- RPC Style Communication ---');

  try {
    const sum = await rpc.call('CALCULATE', [1, 2, 3, 4, 5]);
    console.log('Sum:', sum);

    const doubled = await rpc.call('TRANSFORM', [1, 2, 3, 4, 5]);
    console.log('Doubled:', doubled);

    const filtered = await rpc.call('FILTER', [1, 3, 5, 7, 9, 11]);
    console.log('Filtered:', filtered);
  } catch (error) {
    console.error('RPC Error:', error);
  }
}, 500);

// =============================================================================
// 7. TRANSFERABLE OBJECTS CONCEPT
// =============================================================================

console.log('\n--- Transferable Objects ---');

// Concept demonstration (actual transfer requires browser)
function demonstrateTransfer() {
  // Creating a large buffer
  const buffer = new ArrayBuffer(1024 * 1024); // 1MB
  const view = new Uint8Array(buffer);

  // Fill with data
  for (let i = 0; i < view.length; i++) {
    view[i] = i % 256;
  }

  console.log('Original buffer size:', buffer.byteLength);

  // In a real worker:
  // worker.postMessage(buffer, [buffer]);
  // After transfer, buffer.byteLength would be 0

  // Simulating transfer
  const transferred = buffer.slice(0); // Copy in simulation
  console.log('Transferred buffer size:', transferred.byteLength);

  // In real transfer:
  // console.log('Original after transfer:', buffer.byteLength); // 0
}

demonstrateTransfer();

// =============================================================================
// 8. SHARED ARRAY BUFFER CONCEPT
// =============================================================================

console.log('\n--- SharedArrayBuffer Concept ---');

// SharedArrayBuffer allows true shared memory
// Requires special headers: Cross-Origin-Opener-Policy, Cross-Origin-Embedder-Policy

function demonstrateSharedMemory() {
  // Creating shared memory
  // const shared = new SharedArrayBuffer(1024);
  // const array = new Int32Array(shared);

  // Atomic operations for thread safety
  // Atomics.add(array, 0, 1);
  // Atomics.load(array, 0);
  // Atomics.store(array, 0, 42);
  // Atomics.compareExchange(array, 0, 42, 100);

  console.log('SharedArrayBuffer allows multiple workers to share memory');
  console.log('Use Atomics for thread-safe operations');
}

demonstrateSharedMemory();

// =============================================================================
// 9. INLINE WORKER CREATION
// =============================================================================

console.log('\n--- Inline Worker Pattern ---');

// Creating a worker from a function (browser pattern)
function createInlineWorker(fn) {
  const blob = new Blob([`self.onmessage = ${fn.toString()}`], {
    type: 'application/javascript',
  });

  // In browser: return new Worker(URL.createObjectURL(blob));

  // For demonstration, return mock worker
  return {
    url: 'blob:worker-' + Date.now(),
    terminate: () => {
      // URL.revokeObjectURL(url);
    },
  };
}

const inlineWorkerInfo = createInlineWorker(function (event) {
  const result = event.data.map((x) => x * x);
  self.postMessage(result);
});

console.log('Inline worker created:', inlineWorkerInfo.url);

// =============================================================================
// 10. PROGRESSIVE COMPUTATION
// =============================================================================

console.log('\n--- Progressive Computation ---');

function progressiveWorker(data) {
  const { items, chunkSize } = data;
  const results = [];
  const progress = [];

  for (let i = 0; i < items.length; i += chunkSize) {
    const chunk = items.slice(i, i + chunkSize);
    const processed = chunk.map((x) => x * x);
    results.push(...processed);

    progress.push({
      processed: Math.min(i + chunkSize, items.length),
      total: items.length,
      percent: Math.round(((i + chunkSize) / items.length) * 100),
    });
  }

  return { results, progress };
}

// Simulating progressive updates
async function runProgressiveExample() {
  console.log('\n--- Running Progressive Example ---');

  const items = Array.from({ length: 100 }, (_, i) => i);
  const worker = new MockWorker(progressiveWorker);

  worker.onmessage = (event) => {
    const { results, progress } = event.data;
    console.log('Final results count:', results.length);
    console.log('Progress steps:', progress.length);
    console.log('Last progress:', progress[progress.length - 1]);
  };

  worker.postMessage({ items, chunkSize: 25 });
}

setTimeout(runProgressiveExample, 800);

// =============================================================================
// 11. ERROR HANDLING IN WORKERS
// =============================================================================

console.log('\n--- Worker Error Handling ---');

function errorProneWorker(data) {
  if (data.shouldFail) {
    throw new Error('Intentional worker error');
  }
  return { success: true, data: data.value * 2 };
}

function createResilientWorker(workerFn) {
  return {
    execute(data) {
      return new Promise((resolve, reject) => {
        const worker = new MockWorker(workerFn);
        const timeout = setTimeout(() => {
          worker.terminate();
          reject(new Error('Worker timeout'));
        }, 5000);

        worker.onmessage = (event) => {
          clearTimeout(timeout);
          resolve(event.data);
        };

        worker.onerror = (error) => {
          clearTimeout(timeout);
          reject(error);
        };

        worker.postMessage(data);
      });
    },
  };
}

const resilientWorker = createResilientWorker(errorProneWorker);

setTimeout(async () => {
  console.log('\n--- Resilient Worker Demo ---');

  try {
    const result1 = await resilientWorker.execute({ value: 21 });
    console.log('Success result:', result1);
  } catch (error) {
    console.log('Error:', error.message);
  }

  try {
    const result2 = await resilientWorker.execute({ shouldFail: true });
    console.log('This should not print:', result2);
  } catch (error) {
    console.log('Caught error:', error.message);
  }
}, 1000);

console.log('\n=== Examples loaded. Check output above and below. ===');
Examples - JavaScript Tutorial | DeepML