Skip to content

MessagePort

Use Cap'n Web to talk to Web Workers, iframes, and other same-process contexts.

Updated View as Markdown

Cap’n Web can talk over MessagePorts. In a browser, this lets you use the same RPC model to talk to Web Workers, iframes, and other contexts: no serialization boilerplate, no ad-hoc message protocols with type fields and switch statements.

import { RpcTarget, RpcStub, newMessagePortRpcSession } from 'capnweb';

// Declare our RPC interface.
class Greeter extends RpcTarget {
  greet(name: string): string {
    return `Hello, ${name}!`;
  }
}

// Create a MessageChannel (pair of MessagePorts).
let channel = new MessageChannel();

// Initialize the server on port1.
newMessagePortRpcSession(channel.port1, new Greeter());

// Initialize the client on port2.
using stub: RpcStub<Greeter> = newMessagePortRpcSession<Greeter>(channel.port2);

// Now you can make calls.
console.log(await stub.greet('Alice'));
console.log(await stub.greet('Bob'));

Sending a port somewhere else

In a real-world scenario you’d send one of the two ports to another context. A MessagePort can itself be transferred using postMessage(): window.postMessage(), worker.postMessage(), or even port.postMessage() on some other existing MessagePort.

// Main thread
let worker = new Worker('./worker.js', { type: 'module' });
let channel = new MessageChannel();

// Hand one end to the worker.
worker.postMessage({ rpcPort: channel.port2 }, [channel.port2]);

// Keep the other end and start talking.
using api = newMessagePortRpcSession<WorkerApi>(channel.port1);
console.log(await api.crunchNumbers([1, 2, 3]));
// worker.js
import { RpcTarget, newMessagePortRpcSession } from 'capnweb';

class WorkerApi extends RpcTarget {
  crunchNumbers(values: number[]) {
    return values.reduce((a, b) => a + b, 0);
  }
}

self.addEventListener('message', (event) => {
  if (event.data?.rpcPort) {
    newMessagePortRpcSession(event.data.rpcPort, new WorkerApi());
  }
});

Structured clone

A MessagePort transport can avoid JSON entirely. Custom transports may declare encodingLevel: "structuredClonable" so that messages stay as structured-clonable values, passing through native types where possible while still handling RPC-specific values such as stubs. See Custom transports.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close