javascript 等待电子ipcRenderer中的承诺,通过上下文桥调用

8ljdwjyq  于 2023-03-21  发布在  Java
关注(0)|答案(1)|浏览(153)

我很难通过上下文桥将Electron文档中的Invoke方法应用到我的项目中。
renderer.js中,我需要触发main.js中的异步函数,并等待renderer.js中的响应。
我下面的代码成功触发了main.js中的一个动作。但是,renderer.js中的响应是**undefined**。
我尝试了几种不同的修改,但它越来越糟。请指出我在这里做错了什么!
preload.js

contextBridge.exposeInMainWorld(
    "api", {

        doInvoke: (channel, func) => {
            let validChannels = ["some-channel"];
            if (validChannels.includes(channel)) {
                ipcRenderer.invoke(channel, data).then((result) => {return result})
            }
        },
    }
);

renderer.js

$(document).on('keyup','input', async function(e){
    let response = await window.api.doInvoke(channel='some-channel', data='test-string')
    console.log(response) //--> logs undefined
})

main.js

ipcMain.handle('some-channel', async (event, data) => {
  const result = await doSomeWork(data)
  return result
})

async function doSomeWork(data){
  console.log(data)  //--> logs 'test-string'
  return data + 's'
}
oug3syen

oug3syen1#

虽然有许多小问题可以优化,但主要问题是无法正确地将结果从preload.js脚本返回到index.htmlrenderer.js)文件。
下面是一个最小可重复的示例。
您的main.js文件几乎是完美的。也就是说,如果将doSomeWork分配给result然后返回它,则直接返回它。
main.js(主进程)

// Import required electron modules
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const electronIpcMain = require('electron').ipcMain;

// Import required Node modules
const nodePath = require('path');

// Prevent garbage collection
let window;

function createWindow() {
    const window = new electronBrowserWindow({
        x: 0,
        y: 0,
        width: 800,
        height: 600,
        show: false,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            sandbox: true,
            preload: nodePath.join(__dirname, 'preload.js')
        }
    });

    window.loadFile(nodePath.join(__dirname, 'index.html'))
        .then(() => { window.show(); });

    return window;
}

electronApp.on('ready', () => {
    window = createWindow();
});

electronApp.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        electronApp.quit();
    }
});

electronApp.on('activate', () => {
    if (electronBrowserWindow.getAllWindows().length === 0) {
        createWindow();
    }
});

// ---

electronIpcMain.handle('some-channel', async (event, data) => {
    return await doSomeWork(data);
})

async function doSomeWork(data) {
    console.log(data) //--> logs 'test-string'
    return data + 's'
}

你的preload.js脚本是正确的。不是从.then语句中返回,而是返回invoke函数。这就是问题所在。
preload.js(主流程)

// Import the necessary Electron modules
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;

// Exposed protected methods in the render process
contextBridge.exposeInMainWorld(
    'api', {
        // From render to main and back again
        doInvoke: (channel, data) => {
            let validChannels = ["some-channel"];

            if (validChannels.includes(channel)) {
                return ipcRenderer.invoke('some-channel', data);
            }
        }
    }
);

对于index.html文件,由于ipcRenderer.invoke(channel,...args)返回一个promise,因此不需要使用async/await。相反,只需使用.then语句来处理promise响应。
另外,不要在函数定义中分配channeldata变量。相反,参数可以只是值。
index.html(渲染过程)

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Electron Test</title>
        <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';"/>
    </head>

    <body>
        <label for="input">Input: </label>
        <input type="text" id="input" autofocus>
    </body>

    <script>
        document.getElementById('input').addEventListener('keyup', () => {
            window.api.doInvoke('some-channel', 'test-string')
                .then((response) => {
                    console.log(response); //--> logs 'test-strings'
                });
        })
    </script>
</html>

相关问题