How to add a callback to ipc renderer send
Thanks to unseen_damage for this suggestion. https://github.com/electron/electron/blob/master/docs/api/ipc-main.md#sending-messages
// In main process.
const {ipcMain} = require('electron')
ipcMain.on('asynchronous-message', (event, arg) => {
if(arg === 'ping')
event.sender.send('asynchronous-reply', 'pong');
else
event.sender.send('asynchronous-reply', 'unrecognized arg');
})
// In renderer process (web page).
const {ipcRenderer} = require('electron')
function callAgent(args) {
return new Promise(resolve => {
ipcRenderer.send('asynchronous-message', args)
ipcRenderer.on('asynchronous-reply', (event, result) => {
resolve(result);
})
});
}
In case anybody is still looking for an answer to this question in 2020, a better way to handle replying from the main thread back to the renderer is not to use send
at all, but rather to use ipcMain.handle
and ipcRenderer.invoke
, which make use of async
/await
and return Promises:
main.js
import { ipcMain } from 'electron';
ipcMain.handle('an-action', async (event, arg) => {
// do stuff
await awaitableProcess();
return "foo";
}
renderer.js
import { ipcRenderer } from 'electron';
(async () => {
const result = await ipcRenderer.invoke('an-action', [1, 2, 3]);
console.log(result); // prints "foo"
})();
ipcMain.handle
and ipcRenderer.invoke
are compatible with contextBridge
, allowing you to expose an API to ask the main thread for data from the renderer thread and do something with it once it comes back.
main.js
import { ipcMain, BrowserWindow } from 'electron';
ipcMain.handle('an-action', async (event, arg) => {
// do stuff
await awaitableProcess();
return "foo";
}
new BrowserWindow({
...
webPreferences: {
contextIsolation: true,
preload: "preload.js" // MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY if you're using webpack
}
...
});
preload.js
import { ipcRenderer, contextBridge } from 'electron';
// Adds an object 'api' to the global window object:
contextBridge.exposeInMainWorld('api', {
doAction: arg => ipcRenderer.invoke('an-action', arg);
});
renderer.js
(async () => {
const response = await window.api.doAction([1,2,3]);
console.log(response); // we now have the response from the main thread without exposing
// ipcRenderer, leaving the app less vulnerable to attack
})();