Skip to main content

postMessage() between threads

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:

FromMethodListener
Main thread to workerworker.postMessage()onmessage inside the worker
Worker to main threadpostMessage()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 usedExample
window to iframeiframe.contentWindow.postMessage(data, origin)
iframe to parentwindow.parent.postMessage(data, origin)
between tabsotherWindow.postMessage(data, origin)
Service Worker and main threadnavigator.serviceWorker.controller.postMessage(data)
Web Worker and main threadworker.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 doesExplanation
postMessage()Sends a message between threads and contexts
Works asynchronouslyDoes not block the code
Passes data by copyThrough structured clone
OptimisationTransferable objects, no copying
Two way channelBoth threads can send and receive
Where it is usedWeb 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.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.