MessageChannel / MessagePort
WHATWG Channel Messaging API for communication between execution contexts. MessageChannel creates two connected ports; MessagePort sends and receives messages.
Globals
| Global | Description |
|---|---|
MessageChannel | Creates a pair of connected MessagePorts |
MessagePort | Port for sending/receiving messages |
MessageChannel
Basic Usage
let channel = new MessageChannel();
// Port 1 → Port 2
channel.port1.addEventListener('message', (event) => {
console.log('Port 1 received:', event.data);
});
channel.port2.postMessage('Hello from port 2!');2
3
4
5
6
7
8
Constructor
let channel = new MessageChannel();
// channel.port1 and channel.port2 are connected2
No options — the constructor always creates two connected ports.
MessagePort
postMessage(data)
Send a message to the connected port:
port.postMessage('simple string');
port.postMessage({ key: 'value' });
port.postMessage([1, 2, 3]);2
3
Messages are cloned (not shared). The receiver gets a copy.
Event: 'message'
port.addEventListener('message', (event) => {
console.log('Received:', event.data);
// event.data is the cloned message
// event.ports is an array of transferred MessagePorts (always empty in qzjs)
});2
3
4
5
start()
Start the port. In qzjs, ports start automatically when created — explicit start() is only needed if addEventListener is called after postMessage.
port.start();close()
Close the port, preventing further messages:
port.close();onmessage
Event handler property alternative:
port.onmessage = (event) => {
console.log('Got:', event.data);
};2
3
onmessageerror
Called when a message can't be deserialized:
port.onmessageerror = (event) => {
console.error('Failed to deserialize message');
};2
3
Cross-Context Communication
The primary use case for MessageChannel is communication between qzjs contexts (multi-context mode):
// Context A
let channel = new MessageChannel();
contextB.postMessage({ port: channel.port2 }, [channel.port2]);
channel.port1.addEventListener('message', (event) => {
console.log('Response from context B:', event.data);
});
// Context B (in its own qzjs context)
globalThis.addEventListener('message', (event) => {
let port = event.ports[0];
console.log('Request from context A:', event.data);
port.postMessage('Response from B');
});2
3
4
5
6
7
8
9
10
11
12
13
14
Transferable Objects
When using postMessage with the second argument (transfer list), the ownership of MessagePort objects is transferred:
// Transfer port2 to the receiver — sender loses access
port1.postMessage('take this port', [port2]);
// port2 is now neutered; can't be used by sender2
3
Only MessagePort objects can be transferred. ArrayBuffer transfer is not supported.
Notes
- Messages are cloned via structured clone algorithm (see structuredClone)
- No
BroadcastChannel— useMessageChannelfor 1:1, or build your own pub/sub - Ports are garbage collected when unreferenced
- There's no concept of "origin" or "source" on received messages
event.portsis always populated (empty array if no ports transferred)