Перейти к содержимому

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.js
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js', { scope: '/' })
.then(reg => reg.update()); // проверить обновление сразу
}

Итог: Dedicated Worker — изолированный поток для CPU-задач; Shared Worker — разделяемый между вкладками канал; Service Worker — network proxy с жизненным циклом install/activate.