DevHub Global Solutions | Educativo

Web Workers: procesamiento paralelo en JS

Rompe las limitaciones del hilo único de JavaScript con Web Workers. Aprende a ejecutar tareas intensivas en segundo plano sin bloquear la interfaz de usuario, mejorando rendimiento y experiencia de usuario.
Web Workers: procesamiento paralelo en JS
Diagrama de comunicación entre hilo principal y Web Workers

Conceptos Básicos

Los Web Workers ejecutan código en hilos separados:

Hilo dedicado

Ejecución en background

Comunicación

Vía postMessage/onmessage

Restricciones

Sin acceso a DOM, window o document

// main.js
const worker = new Worker('worker.js');

worker.postMessage({ cmd: 'start', data: 10000 });

worker.onmessage = function(e) {
  console.log('Resultado:', e.data);
};

// worker.js
self.onmessage = function(e) {
  if (e.data.cmd === 'start') {
    const result = heavyCalculation(e.data.data);
    self.postMessage(result);
  }
};

function heavyCalculation(max) {
  let sum = 0;
  for (let i = 0; i < max; i++) {
    // Cálculo intensivo
    sum += Math.sqrt(i) * Math.sin(i);
  }
  return sum;
}

Tipos de Web Workers

Diferentes implementaciones para distintos casos:

Tipo Características Caso de uso
Dedicated Worker Comunicación 1:1 con script padre Tareas específicas
Shared Worker Comunicación multi-contexto (pestañas) Sincronización entre tabs
Service Worker Proxy de red, offline first PWA, caching
Worklet Hooks de bajo rendimiento Animaciones, audio

Casos de Uso Avanzados

Aplicaciones prácticas en proyectos reales:

Cálculos pesados

Matemáticas complejas, simulaciones

Procesamiento de imágenes

Filtros, transformaciones

Manipulación de grandes datasets

Ordenación, análisis

Comunicación en tiempo real

WebSockets, polling intensivo

// Procesamiento de imágenes en worker
function processImageInWorker(imageData) {
  return new Promise((resolve) => {
    const worker = new Worker('image-processor.js');
    
    worker.postMessage(imageData, [imageData.data.buffer]);
    
    worker.onmessage = (e) => {
      resolve(e.data);
      worker.terminate();
    };
  });
}

// Uso con Canvas
const canvas = document.getElementById('miCanvas');
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

processImageInWorker(imageData)
  .then(processedData => {
    ctx.putImageData(processedData, 0, 0);
  });

Mejores Prácticas

Optimización y manejo de errores:

// Pool de workers para evitar sobrecarga
class WorkerPool {
  constructor(size, workerScript) {
    this.size = size;
    this.workers = [];
    this.tasks = [];
    
    for (let i = 0; i < size; i++) {
      const worker = new Worker(workerScript);
      worker.onmessage = this.handleResult.bind(this);
      this.workers.push({ worker, busy: false });
    }
  }
  
  exec(data) {
    return new Promise((resolve) => {
      this.tasks.push({ data, resolve });
      this.processNext();
    });
  }
  
  processNext() {
    const availableWorker = this.workers.find(w => !w.busy);
    if (availableWorker && this.tasks.length > 0) {
      const task = this.tasks.shift();
      availableWorker.busy = true;
      availableWorker.worker.postMessage(task.data);
      availableWorker.taskResolve = task.resolve;
    }
  }
  
  handleResult(e) {
    const workerInfo = this.workers.find(w => w.worker === e.target);
    if (workerInfo) {
      workerInfo.busy = false;
      workerInfo.taskResolve(e.data);
      this.processNext();
    }
  }
}
AT

Sobre el autor

Especialista en rendimiento web y aplicaciones pesadas. Líder de equipo en aplicación de procesamiento de datos con 1M+ usuarios.