Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Чому Web Worker не має доступу до DOM». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Web Worker не бачить DOM, бо DOM не є потокобезпечним і живе тільки в головному (UI) потоці разом з Render Tree.** Будь-яка зміна вузла тягне за собою перерахунок стилів, layout і перемальовування, а ці кроки мають виконуватися в строго визначеному порядку. Якби два потоки правили одне дерево одночасно, з'явилися б гонки даних, браузеру довелося б блокувати головний потік заради синхронізації, і Event Loop став би непередбачуваним. Тому воркер запускається в ізольованій пісочниці без `window`, `document`, `localStorage`, `alert()`, і спілкується з головним потоком тільки через `postMessage()` / `onmessage`. Воркер рахує, а DOM оновлює виключно головний потік. **Ключове:** DOM прив'язаний до рендерингу і однопотоковий за дизайном, тому воркер віддає результат повідомленням, а інтерфейс міняє головний потік.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Web Worker не має доступу до DOM, бо DOM не потокобезпечний і синхронно пов'язаний з рендерингом сторінки, який живе тільки в головному (UI) потоці.** Воркер працює в ізольованій пісочниці і передає результат головному потоку повідомленням, а той уже сам змінює інтерфейс. ## Теорія ### TL;DR - DOM це велике дерево об'єктів, яке браузер тримає в пам'яті і синхронно пов'язує з візуальним відображенням сторінки (layout плюс render). - DOM і Render Tree існують тільки в головному потоці, у воркера свій екземпляр JS-рушія і жодного зв'язку з рендерером. - Одночасний доступ з кількох потоків дав би гонки даних і падіння браузера. - Рендеринг не можна розпаралелити: стилі, layout і paint мають виконуватися в чіткому порядку. - Ізоляція це ще й безпека: немає `window`, `document`, `localStorage`, `alert()`. - Модель взаємодії одна: воркер рахує і надсилає результат, DOM оновлює головний потік. ### Швидкий приклад ```javascript // main.js const worker = new Worker('worker.js'); worker.postMessage('calculate'); worker.onmessage = (e) => { document.querySelector('#result').textContent = e.data; }; ``` ```javascript // worker.js onmessage = (e) => { const result = heavyCalculation(); postMessage(result); }; ``` Воркер робить обчислення і надсилає результат, а головний потік оновлює DOM, якщо це потрібно. ### Де живе DOM і як влаштований браузер DOM (Document Object Model) це величезне дерево об'єктів, яке браузер зберігає в пам'яті і синхронно пов'язує з візуальним відображенням сторінки (layout плюс render). Коли JavaScript щось міняє в DOM (`element.textContent`, `style`, `appendChild` і подібне): - браузер одразу оновлює внутрішні структури, - перебудовує layout, - перемальовує елементи на екрані. І все це відбувається в головному (UI) потоці браузера. Спрощена схема: ```text +--------------------+ | Main thread (UI) | | |- JS Engine | <- has DOM access | |- Render Tree | <- responsible for display | +- Event Loop | +--------------------+ | v +--------------------+ | Web Worker Thread | | +- JS Engine | <- no DOM, no window/document +--------------------+ ``` DOM і Render Tree є тільки в головному потоці. Усі Web Workers це ізольовані потоки: у них своя копія JS-рушія, але немає зв'язку з рендерером сторінки. ### Чому воркерам не можна дати доступ до DOM #### 1. Проблема потокобезпечності DOM не розроблявся для одночасного доступу з кількох потоків. > Якщо два потоки почнуть одночасно міняти один і той самий елемент, виникнуть гонки даних (race conditions) і падіння браузера. JS однопотоковий не випадково: саме це робить DOM-операції детермінованими і безпечними. #### 2. UI-рендеринг не можна розпаралелити Зміна DOM це зміна інтерфейсу. Браузеру треба: - перерахувати стилі (CSSOM), - перебудувати layout, - оновити шари рендера (paint і composite). Ці операції мають відбуватися в строго визначеному порядку. Якби кілька потоків міняли DOM одночасно, браузеру довелося б синхронізувати всі потоки, а це вбило б продуктивність. #### 3. Безпека та ізоляція (sandbox) Web Workers ізольовані в окремій пісочниці: - не бачать `window`, `document`, `localStorage`; - не мають доступу до `alert()`, `confirm()`, `prompt()`; - не можуть напряму читати чи міняти вміст сторінки. Це захищає сайт від випадкової або зловмисної зміни інтерфейсу і даних користувача. #### 4. Модель взаємодії через повідомлення Замість прямого доступу до DOM воркери спілкуються з головним потоком через `postMessage()`. Воркер робить обчислення і надсилає результат, а головний потік вирішує, що саме показати. #### 5. Продуктивність і передбачуваність Якби воркер мав доступ до DOM: - кожна зміна вимагала б синхронізації між потоками; - браузеру довелося б блокувати головний потік заради захисту даних; - Event Loop став би непередбачуваним. Тобто воркери існують саме для того, щоб розвантажити UI, а не щоб втручатися в нього напряму. ### Що воркер може робити замість DOM Воркери не рендерять інтерфейс, але можуть: - рахувати, парсити і фільтрувати великі дані; - працювати з `fetch`, `WebSocket`, `IndexedDB`; - виконувати машинне навчання і криптографію; - обробляти зображення, файли, аудіо; - синхронізувати дані із сервером. Усе це без замороження інтерфейсу. ### Як усе ж оновити DOM через воркер Через повідомлення: ```javascript // worker.js onmessage = () => { postMessage({ type: 'updateUI', text: 'Дані готові!' }); }; ``` ```javascript // main.js worker.onmessage = (e) => { if (e.data.type === 'updateUI') { document.querySelector('#status').textContent = e.data.text; } }; ``` Воркер «каже», що треба зробити, а UI-потік уже сам вносить зміни в DOM. Проста аналогія: > Уявіть, що головний потік це хірург, а Web Worker це асистент у лабораторії. Асистент може рахувати, аналізувати, готувати дані, але не може напряму торкатися пацієнта (DOM). Він просто передає результати хірургу, а той уже акуратно вносить зміни. Підсумок: | Питання | Відповідь | | --- | --- | | Чому воркер не має доступу до DOM? | Бо DOM не потокобезпечний і пов'язаний з UI | | Де знаходиться DOM? | Тільки в головному потоці браузера | | Що робить воркер? | Обчислення, обробку даних, запити | | Як спілкуватися з головним потоком? | Через `postMessage` / `onmessage` | | Хто оновлює DOM? | Тільки головний потік | ### Типові помилки - Звертатися в коді воркера до `document` або `window` і дивуватися помилці `ReferenceError`. У воркера глобальний об'єкт інший, це `self` (`DedicatedWorkerGlobalScope`). - Думати, що обмеження суто формальне і його можна обійти. Обійти не можна: у потоці воркера просто немає ні DOM, ні Render Tree. - Переносити у воркер саме оновлення інтерфейсу, а не обчислення. Виграш дає тільки винесення важких розрахунків, DOM однаково оновлюється в головному потоці. - Надсилати з воркера сотні дрібних повідомлень на кожен крок. Кожне повідомлення це серіалізація і задача в черзі головного потоку, краще накопичити результат і надіслати його пакетом. - Плутати відсутність DOM з відсутністю API взагалі. `fetch`, `WebSocket`, `IndexedDB`, `setTimeout` і `crypto` у воркері доступні. - Розраховувати на `localStorage` для обміну даними. Його у воркері немає, для спільного стану підходить `IndexedDB`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.