refactor(electron): reduce the number of listeners for ipc (#7740)

previously there are quite a lot of api/events handlers registered on ipcMain/ipcRenderer. After this PR, the number should be significantly reduced, which will benefit performance.
This commit is contained in:
pengx17
2024-08-05 13:33:31 +00:00
parent 0acc1bd9e8
commit 0d7de67e01
5 changed files with 104 additions and 58 deletions
@@ -1,5 +1,6 @@
import { app, BrowserWindow, WebContentsView } from 'electron';
import { AFFINE_EVENT_CHANNEL_NAME } from '../shared/type';
import { applicationMenuEvents } from './application-menu';
import { logger } from './logger';
import { sharedStorageEvents } from './shared-storage';
@@ -39,14 +40,14 @@ export function registerEvents() {
return;
}
// .webContents could be undefined if the window is destroyed
win.webContents?.send(chan, ...args);
win.webContents?.send(AFFINE_EVENT_CHANNEL_NAME, chan, ...args);
win.contentView.children.forEach(child => {
if (
child instanceof WebContentsView &&
child.webContents &&
!child.webContents.isDestroyed()
) {
child.webContents?.send(chan, ...args);
child.webContents?.send(AFFINE_EVENT_CHANNEL_NAME, chan, ...args);
}
});
});
+56 -40
View File
@@ -1,5 +1,6 @@
import { ipcMain } from 'electron';
import { AFFINE_API_CHANNEL_NAME } from '../shared/type';
import { clipboardHandlers } from './clipboard';
import { configStorageHandlers } from './config-storage';
import { exportHandlers } from './export';
@@ -31,45 +32,60 @@ export const allHandlers = {
};
export const registerHandlers = () => {
// TODO(@Peng): listen to namespace instead of individual event types
ipcMain.setMaxListeners(100);
for (const [namespace, namespaceHandlers] of Object.entries(allHandlers)) {
for (const [key, handler] of Object.entries(namespaceHandlers)) {
const chan = `${namespace}:${key}`;
const wrapper = async (
e: Electron.IpcMainInvokeEvent,
...args: any[]
) => {
const start = performance.now();
try {
const result = await handler(e, ...args);
logger.debug(
'[ipc-api]',
chan,
args.filter(
arg => typeof arg !== 'function' && typeof arg !== 'object'
),
'-',
(performance.now() - start).toFixed(2),
'ms'
);
return result;
} catch (error) {
logger.error('[ipc]', chan, error);
}
};
// for ipcRenderer.invoke
ipcMain.handle(chan, wrapper);
// for ipcRenderer.sendSync
ipcMain.on(chan, (e, ...args) => {
wrapper(e, ...args)
.then(ret => {
e.returnValue = ret;
})
.catch(() => {
// never throw
});
});
const handleIpcMessage = async (
e: Electron.IpcMainInvokeEvent,
...args: any[]
) => {
// args[0] is the `{namespace:key}`
if (typeof args[0] !== 'string') {
logger.error('invalid ipc message', args);
return;
}
}
const channel = args[0] as string;
const [namespace, key] = channel.split(':');
if (!namespace || !key) {
logger.error('invalid ipc message', args);
return;
}
// @ts-expect-error - ignore here
const handler = allHandlers[namespace]?.[key];
if (!handler) {
logger.error('handler not found for ', args[0]);
return;
}
const start = Date.now();
const realArgs = args.slice(1);
const result = await handler(e, ...realArgs);
logger.debug(
'[ipc-api]',
channel,
realArgs.filter(
arg => typeof arg !== 'function' && typeof arg !== 'object'
),
'-',
Date.now() - start,
'ms'
);
return result;
};
ipcMain.handle(AFFINE_API_CHANNEL_NAME, async (e, ...args: any[]) => {
return handleIpcMessage(e, ...args);
});
ipcMain.on(AFFINE_API_CHANNEL_NAME, (e, ...args: any[]) => {
handleIpcMessage(e, ...args)
.then(ret => {
e.returnValue = ret;
})
.catch(() => {
// never throw
});
});
};