Multi-process model considered harmful AKA too much IPC


#1

I’ve spent the last two months working on an Electron app for personal knowledge repository which supports high quality PDF and HTML annotations.

Initially I was making steady progress but now I’m in a quagmire of IPC.

I think this is starting to look like an anti-pattern.

In one sense the multi-process model is great because apps are separated but if you’re in the SAME app you spend a ton of time thinking about maintaining and sharing state and passing messages back and forth between processes and windows.

Typescript makes things easier because you can at least type the messages so you have a good schema for what’s being sent but state and lifecycle become a huge problem.

  1. What context is this code in? Renderer? Main? It’s hard to keep track.

  2. What if you’re communicating between two windows? What if one of the windows vanishes? Do you restart it? How to you get that window to resume state?

  3. You now have to maintain order to determine when the second window is open and when it’s ready to receive messages.

I mean I guess you could solve #2 and #3 by making your entire app crash if any of the windows are closed. You’re still stuck with IPC but maybe you could make it less hectic.

I think if the ‘window as an isolated process’ model were fixed this wouldn’t be as much of a problem. This way two renderer models could run in the same process.

Additionally, it seems silly for local apps to have two processes (main and renderer) if they’re just going to render and work with local content.

The model is inherited from the web world where it makes sense of course - just not much sense for our typical usage in a desktop model.

I’m not sure the main/renderer model could be broken though unless you embedded Node within Chromium…


#2

The multi-process model isn’t just part of how Electron is built. It’s part of Chrome, and I doubt that it would be trivial to change that. I am not a pro Electron developer, but based on what I do know, I feel like there are workarounds for most of your issues.

You could institute a practice of having global variables that get created when a new process is created, which can then be asked about at any time. If you’re concerned about your code files, the best solution is probably to keep them in distinct folders. All of your “desktop application” code can go in one folder and get referenced by package.json so that Electron runs that when it starts, and that code can look in a separate folder for your web app. Then you will know the context based on which folder your file is in.

What if you’re communicating between two windows? What if one of the windows vanishes? Do you restart it? How to you get that window to resume state?

Atom solves this by constantly serializing important data to storage. That way, you can freeze the whole system and everything you need is kept in JSON waiting to be deserialized.

If it’s important for your UI to have multiple windows that work in concert, you can create child windows to make it easier for your main window to communicate with them. You probably shouldn’t be using IPC for that, if you are.

Additionally, it seems silly for local apps to have two processes (main and renderer) if they’re just going to render and work with local content.

One of the very useful features of Electron is how you can take a fully functional web app and just stick it on your desktop with little work needed. Sometimes you want to do some things with the system, and in that case the local code element will run in the main process and open a window to display the web app when it has done whatever it needs to do. A single process would probably mean that apps that are designed to run in both browser and Electron, such as Discord, Slack, and Calypso, wouldn’t be quite so smooth to develop.