Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «postMessage() між потоками». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`postMessage()` надсилає повідомлення з одного контексту виконання в інший, наприклад з головного потоку у Web Worker або навпаки.** Він не дає прямого доступу до пам'яті іншого потоку: дані серіалізуються алгоритмом Structured Clone і кладуться копією в чергу повідомлень отримувача. Виклик асинхронний і нічого не повертає, отримувач ловить дані через подію `message` (`event.data`). Для великих бінарних даних є Transferable об'єкти, наприклад `ArrayBuffer`: власність над буфером передається без копіювання, а в потоці-відправнику він стає невалідним. Той самий метод працює не лише з воркерами, а й між `window` та iframe, між вкладками і з Service Worker. ```javascript worker.postMessage({ task: 'sum', a: 5, b: 7 }); worker.onmessage = (event) => console.log(event.data); // 12 ``` **Ключове:** message passing замість спільної пам'яті, копія через structured clone, а Transferable об'єкти передають владіння без копіювання.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`postMessage()` це метод, який дозволяє надіслати повідомлення з одного контексту в інший, наприклад з головного потоку у Web Worker або навпаки.** Він не виконує прямого доступу до пам'яті, а передає копію даних асинхронно, через механізм message passing (обмін повідомленнями). ## Теорія ### TL;DR - `postMessage()` кладе повідомлення в чергу іншого контексту і одразу повертає керування, він нічого не повертає. - Дані серіалізуються алгоритмом Structured Clone, тобто інший бік отримує копію, а не посилання. - Отримувач читає дані в обробнику події `message` через `event.data`. - Transferable об'єкти (`ArrayBuffer` та подібні) передають владіння без копіювання, це у 10 до 100 разів швидше на великих даних. - Зв'язок двосторонній: обидва боки можуть і слати, і приймати. - Метод універсальний: Web Worker, iframe, інші вкладки, Service Worker. ### Швидкий приклад ```javascript // main.js const worker = new Worker('worker.js'); // надсилаємо повідомлення воркеру worker.postMessage({ task: 'sum', a: 5, b: 7 }); // отримуємо відповідь worker.onmessage = (event) => { console.log('Worker reply:', event.data); }; ``` ```javascript // worker.js onmessage = (event) => { const { task, a, b } = event.data; if (task === 'sum') { postMessage(a + b); // надсилаємо результат назад } }; ``` ### Як це працює з Web Worker Що відбувається крок за кроком: 1. Головний потік викликає `worker.postMessage(...)`. 2. Повідомлення серіалізується і потрапляє в чергу повідомлень воркера. 3. У воркері спрацьовує подія `onmessage`. 4. Воркер виконує обробку і відповідає через `postMessage()`. 5. Головний потік ловить цю подію через `onmessage`. Усе це відбувається асинхронно: JavaScript не чекає, доки воркер відповість. Зв'язок працює в обидва боки: | Звідки | Метод | Слухач | | --- | --- | --- | | Головний потік у воркер | `worker.postMessage()` | `onmessage` у воркері | | Воркер у головний потік | `postMessage()` | `worker.onmessage` | #### Приклад «відлуння» (echo worker) ```javascript // worker.js onmessage = (event) => { postMessage(`Відлуння: ${event.data}`); }; ``` ```javascript // main.js worker.postMessage('Привіт!'); worker.onmessage = (e) => console.log(e.data); // → "Відлуння: Привіт!" ``` ### Що саме можна передати Передавати можна: - примітиви (`string`, `number`, `boolean`, `null`, `undefined`); - об'єкти (JSON-сумісні); - `ArrayBuffer`, `Blob`, `File`, `ImageBitmap`; - Transferable об'єкти, які передаються без копіювання. #### Transferable Objects Зазвичай дані копіюються під час передачі між потоками (через structured clone). Але для великих бінарних даних це дорого. > Transferable Objects (наприклад, `ArrayBuffer`) можна «передати за посиланням», без копіювання, просто передавши владіння. ```javascript const buffer = new ArrayBuffer(1024); worker.postMessage(buffer, [buffer]); // передаємо як transferable ``` Після передачі `buffer` у головному потоці стає невалідним, бо владіння перейшло воркеру. На великих даних це у 10 до 100 разів швидше. #### Що повертає `postMessage()` Нічого. Він просто ставить повідомлення в чергу іншого боку, а отримувач ловить його через подію `message`: ```javascript worker.onmessage = (event) => { console.log(event.data); }; ``` ### postMessage не тільки для Web Workers Метод універсальний, він працює в різних контекстах браузера: | Де використовується | Приклад | | --- | --- | | **window у iframe** | `iframe.contentWindow.postMessage(data, origin)` | | **iframe у parent** | `window.parent.postMessage(data, origin)` | | **між вкладками** | `otherWindow.postMessage(data, origin)` | | **Service Worker і головний потік** | `navigator.serviceWorker.controller.postMessage(data)` | | **Web Worker і головний потік** | `worker.postMessage(data)` | #### Приклад `window.postMessage()` між сторінками ```javascript // Page A const popup = window.open('https://example.com/popup.html'); popup.postMessage('Hello, page B!', 'https://example.com'); // Page B window.addEventListener('message', (event) => { if (event.origin === 'https://example.com') { console.log('Message received:', event.data); } }); ``` Це дуже зручно для безпечної взаємодії між різними доменами, без порушення політики походження. ### Як це влаштовано під капотом 1. Коли викликається `postMessage()`, дані серіалізуються за допомогою Structured Clone Algorithm. 2. Копія даних потрапляє в чергу отримувача. 3. Коли потік звільняється, браузер викликає обробник `onmessage(event)`. 4. Дані доступні в `event.data`. #### Що всередині `event` ```javascript onmessage = (event) => { console.log(event.data); // самі дані console.log(event.origin); // джерело (для вікон та iframe) console.log(event.ports); // MessagePort, якщо є console.log(event.source); // хто надіслав повідомлення }; ``` Проста аналогія: > Уявіть, що `postMessage()` це поштова служба між офісами. Кожен офіс (потік JS) працює ізольовано. Коли один хоче передати дані іншому, він кладе лист (повідомлення) у скриньку (`postMessage`). Інший офіс отримує лист тоді, коли має на це час (`onmessage`). Підсумок: | Що робить | Пояснення | | --- | --- | | `postMessage()` | Надсилає повідомлення між потоками та контекстами | | **Працює асинхронно** | Не блокує код | | **Передає дані копією** | Через structured clone | | **Оптимізація** | Transferable об'єкти, без копіювання | | **Двосторонній зв'язок** | Обидва потоки можуть слати і приймати | | **Де використовується** | Web Workers, iframe, вікна, Service Workers | ### Типові помилки - Думати, що інший бік отримає той самий об'єкт. Він отримує копію, тож зміни в ньому не видно відправнику. - Використовувати `buffer` після того, як його передали як transferable. Він уже невалідний, бо владіння перейшло іншому потоку. - Не перевіряти `event.origin` у `window.addEventListener('message', ...)`. Повідомлення може надіслати будь-яка сторінка, і без перевірки це діра в безпеці. - Передавати `'*'` як targetOrigin у `postMessage(data, '*')` для чутливих даних. Треба вказувати конкретне походження. - Передавати функції, DOM-вузли або екземпляри класів з методами. Structured clone їх не підтримує і кине `DataCloneError`. - Очікувати, що `postMessage()` щось поверне або що відповідь прийде синхронно. Він повертає `undefined`, а відповідь прийде окремою подією.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.