Which ipcRenderer.invoke promise settles when the matched ipcMain.handle never replies?
0 reputation · 10 Oct 2021, 11:51 UTC
0 reputation · 10 Oct 2021, 11:51 UTC
The goal is to characterize the runtime behavior of Electron's ipcRenderer.invoke when the corresponding ipcMain.handle never transmits a reply. This scenario arises in long-running or faulted main-process handlers where no explicit resolution occurs. Understanding whether the promise remains pending, rejects after an implicit timeout, or causes renderer resource retention is essential for designing robust inter‑process communication.
A primary constraint is that Electron provides no built‑in timeout or cancellation for invoke; the Promise returned by ipcRenderer.resolve only settles when the handler calls reply(). Without a handler, the promise rejects immediately, but a silent non‑response leaves the promise in a pending state indefinitely. The renderer lacks API‑level mechanisms to distinguish a slow handler from a dropped connection, and pending promises may retain renderer objects, potentially blocking UI logic that awaits them.
35275 reputation · 10 Oct 2021, 19:05 UTC
When an ipcMain.handle never sends a reply, the ipcRenderer.invoke promise stays pending forever. Electron’s IPC layer does not impose a timeout or automatic rejection; the promise only settles when the main process calls event.reply() or returns a value from the handler.
Promise object remains in memory.Because the renderer cannot detect a silent main‑process handler, you must add a client‑side timeout wrapper:
function safeInvoke(channel, ...args) {
const timeoutMs = 5000; // adjust as needed
return Promise.race([
ipcRenderer.invoke(channel, ...args),
new Promise((_, reject) => setTimeout(() => reject(new Error('invoke timed out')), timeoutMs))
]);
}
Note that rejecting the wrapper does not cancel the main‑process handler; it may continue running and eventually send a reply that will be ignored by the renderer.
While the promise is pending, the renderer’s IPC layer holds a reference to the callback closure. Any objects captured by that closure—such as DOM nodes or large buffers—remain reachable, potentially delaying garbage collection of the entire renderer context until the page is navigated, reloaded, or the renderer process is killed.
main.js, register a handler that never replies:
ipcMain.handle('slow', () => {/* intentionally no reply */});
ipcRenderer.invoke('slow').then(console.log).catch(console.error);
safeInvoke above and confirm that it rejects after the timeout while the raw promise remains pending.The behavior can vary slightly across Electron major releases. If you notice a different outcome, please share the exact Electron version you’re using.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.