Skip to main content

SharedWorker у JavaScript

SharedWorker це потік JavaScript, який можуть використовувати кілька контекстів (вкладок, вікон, iframe) одночасно, якщо вони завантажені з одного origin (протокол плюс хост плюс порт). На відміну від звичайного Worker, який «помирає» разом із закритою вкладкою, SharedWorker живе, доки підключене хоча б одне вікно.

Теорія

TL;DR

  • Один потік обслуговує всі вкладки та iframe одного origin.
  • Створюється через new SharedWorker('shared-worker.js'), канал це shared.port.
  • Кожне нове підключення приходить у воркер подією onconnect з власним MessagePort.
  • Порт треба активувати: port.start(), якщо ви використовуєте addEventListener замість onmessage.
  • Живе, доки підключений хоча б один клієнт, потім браузер його зупиняє.
  • Доступу до DOM немає, дані передаються копією через structured clone.

Швидкий приклад

Код у shared-worker.js:

javascript
// shared-worker.js onconnect = (event) => { const port = event.ports[0]; console.log('New connection'); port.postMessage('Hello, tab!'); port.onmessage = (e) => { console.log('Message from a tab:', e.data); // надсилаємо відповідь назад port.postMessage(`Received: ${e.data}`); }; };

Код на кожній вкладці (main.js):

javascript
// main.js const shared = new SharedWorker('shared-worker.js'); const port = shared.port; // активуємо канал port.start(); // слухаємо відповіді від воркера port.onmessage = (e) => { console.log('SharedWorker reply:', e.data); }; // надсилаємо повідомлення воркеру port.postMessage('Hello from a tab!');

Що станеться:

  • усі вкладки під'єднаються до одного й того самого воркера;
  • кожна з них отримуватиме повідомлення через свій port;
  • воркер може координувати їх, наприклад розсилати спільні дані всім.

Чим SharedWorker відрізняється від звичайного Worker

ОсобливістьWorker (Dedicated)SharedWorker
ВикористовуєтьсяОднією сторінкоюКількома вкладками та вікнами
Створюєтьсяnew Worker('worker.js')new SharedWorker('worker.js')
Канал зв'язкуpostMessage напрямуЧерез об'єкт port
Час життяДоки відкрита вкладкаДоки підключений хоча б один контекст
Доступний з інших вкладокНіТак, якщо той самий origin

Схема взаємодії:

text
[ Tab 1 ] [ Tab 2 ] [ Iframe ] | | | +--------> [ SharedWorker Thread ] <+ ^ | | (ports[]) v business logic

Усі вкладки під'єднуються до одного потоку воркера, а воркер обмінюється повідомленнями з кожною вкладкою через її port.

Як працює onconnect і MessagePort

Коли нова вкладка викликає new SharedWorker(), браузер не створює новий потік, якщо воркер уже існує. Він просто викликає подію onconnect і передає туди об'єкт MessagePort, який і є каналом зв'язку.

Отже, один SharedWorker може керувати десятками портів, тобто десятками вкладок.

Розсилка всім вкладкам

javascript
// shared-worker.js const ports = []; onconnect = (e) => { const port = e.ports[0]; ports.push(port); port.start(); port.onmessage = (event) => { // розсилаємо повідомлення всім підключеним вкладкам for (const p of ports) { p.postMessage(`From a tab: ${event.data}`); } }; };

Тепер, якщо відкрити дві вкладки:

  • вкладка 1 надішле port.postMessage("Hi"),
  • обидві вкладки отримають "From a tab: Hi".

Це вже справжній чат між вкладками.

Де це корисно

СценарійЧому SharedWorker корисний
Спільний чат між вкладкамиможна синхронізувати повідомлення
Фонова обробка даниходна вкладка рахує, результат отримують усі
Спільна підписка на WebSocketодне підключення на всі вкладки, економія ресурсів
Спільна логіка кешу та IndexedDBодин фон для всіх
Синхронізація станунаприклад, спільний токен авторизації між вкладками

Що важливо пам'ятати

  • SharedWorker працює тільки в межах одного origin (https://example.com не дорівнює http://example.com).
  • Це не Service Worker, типи воркерів різні і взаємозамінними не є.
  • Підтримка неповна: у деяких старих браузерах і в Safari на iOS його немає.
  • Спілкування йде через port.postMessage() і port.onmessage.

Безпека та передача даних

  • Воркери ізольовані: немає доступу до DOM, window, document.
  • Дані передаються копією (structured clone).
  • Для швидкого обміну великими бінарними даними можна використовувати Transferable об'єкти (ArrayBuffer).

Проста аналогія:

Уявіть, що SharedWorker це офісний сервер у компанії. Кожен працівник (вкладка) може до нього під'єднатися. Сервер зберігає спільні дані і розсилає оновлення всім. Доки працює хоча б один працівник, сервер живий. Коли всі закрили вкладки, сервер автоматично вимикається.

Підсумок:

ОсобливістьОпис
Що робитьДає кільком сторінкам одного сайту спільний потік JS
Головна перевагаСпільні логіка і дані між вкладками
КомунікаціяЧерез MessagePort (port.postMessage / port.onmessage)
Життєвий циклДоки відкритий хоча б один клієнт
Немає доступу до DOMТільки обмін повідомленнями
Типові сценаріїСпільний WebSocket, кеш, синхронізація стану, чат між вкладками

Типові помилки

  • Забути port.start(). Якщо ви підписуєтеся через port.addEventListener('message', ...), порт лишається неактивним і повідомлення не приходять. Присвоєння port.onmessage запускає порт неявно.
  • Звертатися до shared.postMessage(...) замість shared.port.postMessage(...). У SharedWorker метод живе на порту, а не на самому об'єкті воркера.
  • Розраховувати, що SharedWorker побачить вкладку з іншого origin або з іншого шляху скрипта. Воркер ідентифікується парою origin плюс URL скрипта, інакше це буде вже інший воркер.
  • Не прибирати закриті порти зі списку ports. Порти мертвих вкладок накопичуються, і розсилка починає писати в нікуди.
  • Плутати SharedWorker із Service Worker. Перший це спільний обчислювальний потік, другий це мережевий проксі з власним життєвим циклом.
  • Покладатися на SharedWorker як на єдиний механізм синхронізації вкладок. Через нерівну підтримку варто мати запасний варіант, наприклад BroadcastChannel.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.