Suggest an editImprove this articleRefine the answer for “postMessage() between threads”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**`postMessage()` sends a message from one execution context to another, for example from the main thread into a Web Worker or back.** It gives no direct access to the other thread's memory: the data is serialised by the Structured Clone algorithm and placed as a copy into the receiver's message queue. The call is asynchronous and returns nothing; the receiver picks the data up through the `message` event (`event.data`). For large binary payloads there are Transferable objects such as `ArrayBuffer`: ownership of the buffer moves without copying, and the buffer becomes unusable in the sending thread. The same method works beyond workers too, between `window` and an iframe, between tabs, and with a Service Worker. ```javascript worker.postMessage({ task: 'sum', a: 5, b: 7 }); worker.onmessage = (event) => console.log(event.data); // 12 ``` **Key point:** message passing instead of shared memory, a structured clone copy by default, and Transferable objects to move ownership without copying.Shown above the full answer for quick recall.Answer (EN)Image**`postMessage()` is the method that sends a message from one context to another, for example from the main thread into a Web Worker or back.** It does not touch the other side's memory directly; it hands over a copy of the data asynchronously through the message passing mechanism. ## Theory ### TL;DR - `postMessage()` puts a message into another context's queue and returns immediately; it returns nothing. - The data is serialised by the Structured Clone algorithm, so the other side gets a copy, not a reference. - The receiver reads the data in a `message` event handler through `event.data`. - Transferable objects (`ArrayBuffer` and friends) move ownership without copying, which is 10 to 100 times faster on large payloads. - The channel is two way: both sides can send and receive. - The method is universal: Web Worker, iframe, other tabs, Service Worker. ### Quick example ```javascript // main.js const worker = new Worker('worker.js'); // send a message to the worker worker.postMessage({ task: 'sum', a: 5, b: 7 }); // receive the reply 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); // send the result back } }; ``` ### How it works with a Web Worker Step by step: 1. The main thread calls `worker.postMessage(...)`. 2. The message is serialised and placed into the worker's message queue. 3. The `onmessage` event fires inside the worker. 4. The worker does its processing and replies through `postMessage()`. 5. The main thread catches that event through `onmessage`. All of this is asynchronous: JavaScript does not wait for the worker to answer. The channel works in both directions: | From | Method | Listener | | --- | --- | --- | | Main thread to worker | `worker.postMessage()` | `onmessage` inside the worker | | Worker to main thread | `postMessage()` | `worker.onmessage` | #### An echo worker example ```javascript // worker.js onmessage = (event) => { postMessage(`Echo: ${event.data}`); }; ``` ```javascript // main.js worker.postMessage('Hello!'); worker.onmessage = (e) => console.log(e.data); // -> "Echo: Hello!" ``` ### What exactly can be sent You can send: - primitives (`string`, `number`, `boolean`, `null`, `undefined`); - objects (JSON compatible); - `ArrayBuffer`, `Blob`, `File`, `ImageBitmap`; - Transferable objects, which are handed over without copying. #### Transferable Objects Normally data is copied when it crosses threads (through structured clone). For large binary payloads that is expensive. > Transferable Objects (for example `ArrayBuffer`) can be "passed by reference", without copying, simply by moving ownership. ```javascript const buffer = new ArrayBuffer(1024); worker.postMessage(buffer, [buffer]); // hand it over as transferable ``` After the transfer, `buffer` becomes unusable in the main thread, because ownership moved to the worker. On large payloads this is 10 to 100 times faster. #### What `postMessage()` returns Nothing. It simply queues the message for the other side, and the receiver catches it through the `message` event: ```javascript worker.onmessage = (event) => { console.log(event.data); }; ``` ### postMessage is not only for Web Workers The method is universal and works across different browser contexts: | Where it is used | Example | | --- | --- | | **window to iframe** | `iframe.contentWindow.postMessage(data, origin)` | | **iframe to parent** | `window.parent.postMessage(data, origin)` | | **between tabs** | `otherWindow.postMessage(data, origin)` | | **Service Worker and main thread** | `navigator.serviceWorker.controller.postMessage(data)` | | **Web Worker and main thread** | `worker.postMessage(data)` | #### A `window.postMessage()` example between pages ```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); } }); ``` This is very handy for safe interaction between different domains, without breaking the same origin policy. ### How it works under the hood 1. When `postMessage()` is called, the data is serialised with the Structured Clone Algorithm. 2. A copy of the data is placed into the receiver's queue. 3. When that thread is free, the browser calls the `onmessage(event)` handler. 4. The data is available in `event.data`. #### What is inside `event` ```javascript onmessage = (event) => { console.log(event.data); // the data itself console.log(event.origin); // the source (for windows and iframes) console.log(event.ports); // MessagePort, if any console.log(event.source); // who sent the message }; ``` A simple analogy: > Imagine `postMessage()` is a postal service between offices. Every office (a JS thread) works in isolation. When one wants to hand data to another, it drops a letter (the message) into the mailbox (`postMessage`). The other office reads the letter when it has time (`onmessage`). Summary: | What it does | Explanation | | --- | --- | | `postMessage()` | Sends a message between threads and contexts | | **Works asynchronously** | Does not block the code | | **Passes data by copy** | Through structured clone | | **Optimisation** | Transferable objects, no copying | | **Two way channel** | Both threads can send and receive | | **Where it is used** | Web Workers, iframes, windows, Service Workers | ### Common mistakes - Assuming the other side receives the same object. It receives a copy, so mutations there are invisible to the sender. - Using a `buffer` after it was handed over as transferable. It is already detached, because ownership moved to the other thread. - Not checking `event.origin` inside `window.addEventListener('message', ...)`. Any page can send a message, and skipping that check is a security hole. - Passing `'*'` as the targetOrigin in `postMessage(data, '*')` for sensitive data. Name the concrete origin instead. - Sending functions, DOM nodes or class instances with methods. Structured clone does not support them and throws `DataCloneError`. - Expecting `postMessage()` to return something or to answer synchronously. It returns `undefined`, and the reply arrives as a separate event.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.