delight-rpc / worker-threads

🌿
https://www.npmjs.com/package/@delight-rpc/worker-threads
MIT License
0 stars 0 forks source link

Doesn't support Proxy in arguments. #4

Closed StNekroman closed 2 years ago

StNekroman commented 2 years ago

I have

api.someMethod(1, 2, {
   prop: 1
});

I run it from inside VM2. VM2 replaces all external things with Proxy. So, instead of {prop1: 1} worker-threads will receive instanceof Proxy, which is not transferable between ports (not serializable) Any thougts?

As dirty hack that cna be fixed by JSON.parse(JSON.stringify()) But what if I want pass a function as callback? Like

 {
   prop: 1,
  onSomething: () => {console.log("something happened");};
}
BlackGlory commented 2 years ago

If Proxy cannot be passed between two ports, then serializing it is the only way to go. The only thing I can do is provide a parameter to help you with the serialization.

For functions, I think probably no RPC framework would support using functions as arguments, although serializing JavaScript functions is possible, but it almost certainly makes the code stink.

BlackGlory commented 2 years ago

The newly added postMessage option allows you to perform a deep copy of the request/response before it is sent, which should solve the Proxy problem. I tried adding serialization options, but in the end found them really unnecessary, postMessage should solve most of the problems, because of structured clone.

BlackGlory commented 2 years ago

Another option has been added: receiveMessage, now you have full control over the format of the intermediate data.

StNekroman commented 2 years ago

Indeed, adding generic callback is good solution which should work for many use cases.

const rpcDiscordApi = createClient<DiscordApi>(proxyData.discordApiPort, {
        postMessage: (port: MessagePort | Worker, request: IRequest<unknown>) => {
            for (let i = 0; i < request.params.length; i++) {
                if (NodeUtils.isProxy(request.params[i])) {
                    request.params[i] = JSON.parse(JSON.stringify(request.params[i]));
                }
            }
            port.postMessage(request);
        }
    });

Works fine, serializes proxies to plain object.

Now regarding callbacks. Serializing them is evil, because callback must be executed in context (on the side) where it was created. Obviously because that lamdba can hold local references to another functions and local state. When I told about them I expected RPC-like behavior, where callback remains on the side where it was created and only triggered by backward event from RPC. But this is IPC already, not RPC. Which sounds out of scope of current project. But anyway... after additional thinking, I come to mind thta I don't need such callbacks at all. Becuase my requires is - to survive service restarts. Afte restart all existing lambdas in memory will be killed (no listener will be available after restart) So I will go with plain events: user did action on one side --> event has sent to another side --> another side is responsible to wire that event and perform callback action.