Skip to content

Observable IPC Channels

Tldr

Transform main-to-renderer communication channels into observables that you can process and subscribe to.

Main Process

The main process sends some data to the renderer process on the count channel every 500ms.

main.js
const {app, BrowserWindow} = require('electron');
const path = require('path');

app.whenReady().then(async () => {
  const bwin = new BrowserWindow({
    width: 600,
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true,
      preload: path.resolve(__dirname, 'preload.js')
    }
  });

  await bwin.loadFile('renderer.html');
  bwin.show();
  bwin.webContents.openDevTools();

  let count = 0;
  setInterval(() => {
    bwin.webContents.send('count', count++); // Send to renderer process.
  }, 500);
});

Preload Script

The on method returns a pair of functions to be used with RxJS's fromEventPattern function.

preload.js
const {contextBridge, ipcRenderer} = require('electron');

contextBridge.exposeInMainWorld('MY_APP', {
  on: channel => ([
    (handler) => { // addListener
      function _handler(ev, ...args) {
        console.log(`Received from channel '${channel}':`, ...args);
        handler(...args);
      }
      console.log(`Start observing from ipcRenderer.on('${channel}')`);
      ipcRenderer.on(channel, _handler);
      return _handler;
    }, //    |
        // +--+
        // |
        // v
    (_, _handler) => { // removeListener
      console.log(`Stop observing from ipcRenderer.on('${channel}')`);
      ipcRenderer.removeListener(channel, _handler);
    }
  ])
});

Renderer Page

We use the pair of functions returned by MY_APP.on('count') with RxJS's fromEventPattern function to get an observable on that channel. Every time the main process emits on that channel that observable will emit the same thing.

renderer.html
<html>

<head>
  <style>
    body {background-color:black;color:limegreen}
  </style>
  <script src="https://unpkg.com/rxjs@7.5.5/dist/bundles/rxjs.umd.min.js"></script>
</head>

<body>
  <div id="response"></div>
  <script>
    const {fromEventPattern} = rxjs;
    const {take} = rxjs.operators;

    const [addListener, removeListener] = window.MY_APP.on('count');
    const count$ = fromEventPattern(addListener, removeListener).pipe(
      take(5) // Unsubscribe automatically after 5 emissions.
    );

    count$.subscribe((count) => {
      document.querySelector('#response').innerHTML += `${count}<br>`;
    });
  </script>
</body>

</html>

Screenshot