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:
// 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):
// 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 |
Схема взаємодії:
[ Tab 1 ] [ Tab 2 ] [ Iframe ]
| | |
+--------> [ SharedWorker Thread ] <+
^ |
| (ports[])
v
business logicУсі вкладки під'єднуються до одного потоку воркера, а воркер обмінюється повідомленнями з кожною вкладкою через її port.
Як працює onconnect і MessagePort
Коли нова вкладка викликає new SharedWorker(), браузер не створює новий потік, якщо воркер уже існує. Він просто викликає подію onconnect і передає туди об'єкт MessagePort, який і є каналом зв'язку.
Отже, один SharedWorker може керувати десятками портів, тобто десятками вкладок.
Розсилка всім вкладкам
// 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.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.