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.
Diagrama de comunicación entre hilo principal y Web Workers
Contenido del Artículo
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();
}
}
}