Skip to content

Instantly share code, notes, and snippets.

@benoitlahoz
Last active May 26, 2024 09:19
Show Gist options
  • Save benoitlahoz/de6c41f5024d111ce37034efe1bf8443 to your computer and use it in GitHub Desktop.
Save benoitlahoz/de6c41f5024d111ce37034efe1bf8443 to your computer and use it in GitHub Desktop.
IPC communication in Electron with client-side callback.
// Renderer.
/**
* Describes the function used to call `invoke` on ipcRenderer.
*/
type InvokeFn = (channel: string, ...args: any[]) => Promise<any>;
/**
* The `invoke` function returned by the `exposeInMainWorld` of the preload script
* or by `ipcRenderer` itself.
*/
let apiInvoke: InvokeFn | undefined;
/**
* Register the function to `invoke` the main process.
*
* @param { InvokeFn } invoke The function that will be called to trigger `handle` in the main process.
*/
export const register = (invoke: InvokeFn): void => {
if (apiInvoke) {
console.warn("Already registered API's `invoke` function is being overwritten.");
}
apiInvoke = invoke;
};
/**
* Invoke a handler of the main process, and trigger the provided callback on response.
*
* @param { string } channel The channel to invoke.
* @param { ...any[] } args Any data to send to the main process handler, and a callback at the end.
* The callback will be called when the result is returned by the main process.
*/
export const emit = async (channel: string, ...args: any[]): Promise<void> => {
if (!apiInvoke) {
throw new Error(`API's 'invoke' function was not registered.`);
}
if (args.length > 0) {
// Get the last arguments that may be a callback or NoOp.
const maybeCallback = typeof args[args.length - 1] === 'function' ? args[args.length - 1] : (..._: any[]) => {};
// Get the data to pass to the IPC.
const data = [...args];
data.splice(data.length - 1, 1);
const res = await apiInvoke(channel, ...data);
maybeCallback(res);
return;
}
// Invoke without data.
await apiInvoke(channel);
};
// Main process.
import { ipcMain } from 'electron';
ipcMain.handle('ping', async (event: IpcMainInvokeEvent, data: string) => {
return `pong: ${data}`;
});
import { ipcRenderer } from 'electron';
import { register, emit } from 'ipc-emit';
// Register the ipcRenderer.invoke function to be used in 'emit'.
register(ipcRendererr.invoke);
(async () => {
await emit('ping', 'foo', (res: string) => {
console.log(res); // pong: foo
}
})();
import { register, emit } from 'ipc-emit';
const invoke = window['MyAPI'].invoke;
// Register the preload script API 'invoke' function to be used in 'emit'.
register(invoke);
(async () => {
await emit('ping', 'foo', (res: string) => {
console.log(res); // pong: foo
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment