Web Workers / Shared Workers / Service Workers
Web Workers позволяют выполнять JS в фоновом потоке без блокировки main thread; различие между Dedicated, Shared и Service Workers — классический вопрос senior-уровня, так как каждый тип решает разные задачи и имеет принципиально разный жизненный цикл.
// Dedicated Web Worker: изолированный поток для тяжёлых вычислений// --- worker.js ---self.addEventListener('message', ({ data }) => { // Нет доступа к DOM, window, document — только WorkerGlobalScope const result = data.items.reduce((acc, x) => acc + x ** 2, 0); // тяжёлые расчёты self.postMessage({ result, id: data.id });});
// --- main.js ---const worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' });
worker.postMessage({ id: 1, items: largeArray });worker.addEventListener('message', ({ data }) => console.log('result:', data.result));worker.addEventListener('error', e => console.error('Worker error:', e.message));worker.terminate(); // явное завершение (не GC)
// Transferable objects: zero-copy передача ArrayBuffer (не structuredClone)const buf = new Float64Array(1_000_000).buffer; // 8 МБworker.postMessage({ buf }, [buf]); // buf передан → в main = detached (0 байт)// Shared Worker: один инстанс разделяется между всеми вкладками одного origin// --- shared-worker.js ---const clients = new Set();self.onconnect = ({ ports: [port] }) => { clients.add(port); port.start(); port.onmessage = ({ data }) => { // broadcast всем подключённым вкладкам for (const p of clients) p.postMessage({ from: 'sw', payload: data }); };};
// --- main.js (каждая вкладка) ---const sw = new SharedWorker('./shared-worker.js');sw.port.start();sw.port.addEventListener('message', ({ data }) => console.log(data));sw.port.postMessage({ action: 'sync', ts: Date.now() });// Shared Worker живёт, пока хотя бы одна вкладка удерживает соединение// Service Worker: network proxy + PWA offline + background sync// --- sw.js ---const CACHE_NAME = 'app-v2';const PRECACHE = ['/', '/index.html', '/app.js', '/style.css'];
self.addEventListener('install', e => e.waitUntil(caches.open(CACHE_NAME).then(c => c.addAll(PRECACHE))));self.addEventListener('activate', e => e.waitUntil( caches.keys() .then(keys => Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k)))) .then(() => self.clients.claim()) // взять управление без перезагрузки ));self.addEventListener('fetch', e => { // Stale-while-revalidate: сразу отдаём кэш, фоново обновляем e.respondWith( caches.open(CACHE_NAME).then(async cache => { const cached = await cache.match(e.request); const fresh = fetch(e.request).then(r => { cache.put(e.request, r.clone()); return r; }); return cached ?? fresh; }) );});
// Регистрация в main.jsif ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js', { scope: '/' }) .then(reg => reg.update()); // проверить обновление сразу}Итог: Dedicated Worker — изолированный поток для CPU-задач; Shared Worker — разделяемый между вкладками канал; Service Worker — network proxy с жизненным циклом install/activate.