如何添加回调到ipc渲染器发送

问题描述 投票:0回答:3

谷歌搜索说你可以向它添加回调,但文档只是说“arg1,arg2,arg3”等。

他们也有sendSync,但我不想在发送事件时阻塞[我们试图通过浏览器做尽可能多的工作,因为在节点中编写客户端代码似乎有点愚蠢]。

如果创建者有 sendSync,那么他们肯定有一个带有回调的版本,或者更好的是承诺。

我希望能够做的一些事情的例子:

//callback
ipcRenderer.send('anaction', '[1, 2, 3]', function() { console.log('done anaction') });
//promise
ipcRenderer.send('anaction', '[1, 2, 3]')
    .then(function() { console.log('done anaction') });

//sync exists, but it blocks. I'm looking for a non-blocking function
ipcRenderer.sendSync('anacount', '[1, 2, 3]')
console.log('done anaction');
javascript electron
3个回答
91
投票

如果有人在 2020 年仍在寻找这个问题的答案,处理从主线程返回渲染器的更好方法是根本不使用

send
,而是使用
ipcMain.handle
ipcRenderer.invoke
,利用
async
/
await
并返回 Promise:

main.js

import { ipcMain } from 'electron';

ipcMain.handle('an-action', async (event, arg) => {
    // do stuff
    await awaitableProcess();
    return "foo";
}

渲染器.js

import { ipcRenderer } from 'electron';

(async () => {
    const result = await ipcRenderer.invoke('an-action', [1, 2, 3]);
    console.log(result); // prints "foo"
})();

ipcMain.handle
ipcRenderer.invoke
contextBridge
兼容,允许您公开 API 向主线程请求来自渲染器线程的数据,并在数据返回后对其执行某些操作。

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
    }
    ...
});

预加载.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)
});

渲染器.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    
})();

20
投票

感谢 unseen_damage 的建议。 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);
        })
    });
}

0
投票

在 @tobeyblaber 之上,我使用调用发布了渲染器端 Gist,该调用在 Electron

IpcMain
handle
响应上调用回调。

代码

/**
 * 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 } data Any data to send to the main process handler.
 * @param { (...args: any[]) => void } callback The callback to call when the result is returned by the main process.
 */
export const emit = async (channel: string, data?: any, callback?: (...args: any[]) => void) => {
  if (!apiInvoke) {
    throw new Error(`API's 'invoke' function was not registered.`);
  }
  const res = await apiInvoke(channel, data);
  if (callback) callback(res);
};

使用

// Main process.

import { ipcMain } from 'electron';

ipcMain.handle('ping', async (event: IpcMainInvokeEvent, data: string) => {
  return `pong: ${data}`;
});
// Renderer process unsafe.

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
  }
})();

// Renderer process with context isolation.

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
  }
})();
© www.soinside.com 2019 - 2024. All rights reserved.