Skip to content

TransferList Experimentation #1

@kgullion

Description

@kgullion

Comlink uses MessagePorts internally for proxy endpoints and Electron has MessageChannel support via MessageChannelMain now.

By patching the MessagePortMain, it can now be used in main.js as a Comlink endpoint. This seems to work pretty well in my limited testing (example below).

Unfortunately, I've been unable to get proxy working. I was hoping replacing the proxyTransferHandler would be enough but while the MessagePorts are transferable, it seems they are not StructuredCloneable :(

I'm guessing it would be possible to get proxy working if we had access to the transferred MessagePort itself in deserialize. Not sure where to go from here but it seems potentially doable. Thoughts?

// main.js

const proxyTransferHandler = {
  canHandle: (val) => !!val && val[comlink.proxyMarker],
  serialize(obj) {
    const { port1, port2 } = new MessageChannelMain();
    console.log(obj, port1, port2);
    comlink.expose(obj, patchMessagePort(port1));
    return [port2, [port2]];
  },
  deserialize(port) {
    console.log(port);
    port.start();
    return comlink.wrap(patchMessagePort(port));
  },
};

comlink.transferHandlers.set("proxy", proxyTransferHandler);

function patchMessagePort(port) {
  if (!port.addEventListener) port.addEventListener = port.on;
  if (!port.removeEventListener) port.removeEventListener = port.off;
  return port;
}

ipcMain.on("comlink1", ({ ports }) => {
  const port = ports[0];
  const obj = { log: console.log, inc: (a) => a + 1, callWith1: (cb) => cb(1) };
  comlink.expose(obj, patchMessagePort(port));
});

ipcMain.on("comlink2", ({ ports }) => {
  const port = ports[0];
  const remote = comlink.wrap(patchMessagePort(port));
  remote.log("hello from main");
  console.log("4+1=", remote.inc(4));
  // console.log('cb', remote.callWith2(comlink.proxy((a)=>a+1)))
});
// preload.js

function comlinkExample1() {
  const { port1, port2 } = new MessageChannel();
  ipcRenderer.postMessage("comlink1", null, [port2]);

  const remote = wrap(port1);
  remote.log("hello from preload");
  console.log("2+1=", remote.inc(2));
  // console.log('cb', remote.callWith1(proxy((a)=>a+1)))
}
comlinkExample1();

function comlinkExample2() {
  const { port1, port2 } = new MessageChannel();
  ipcRenderer.postMessage("comlink2", null, [port2]);

  const obj = { log: console.log, inc: (a) => a + 1, callWith2: (cb) => cb(2) };
  expose(obj, port1);
}
comlinkExample2();

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions