Electron with React - ipcRenderer接收到来自ipcMain的上一条消息的回复?

lrl1mhuk  于 2023-05-11  发布在  Electron
关注(0)|答案(1)|浏览(236)

所以我对Electron还是个新手,我的目标是拥有一个完全离线的应用程序,可以有效地查询和显示加载的SQLite文件的结果(因此这里的SQL实践是有问题的)。
我能够查询我的数据库并获得预期的回报。但是,当我连续进行类似这样的调用时,第二个查询得到的结果与前一个调用相同。这是我的渲染器(React)的一部分。第一个查询通常与预期的一样,但是第二个res与第一个查询相同。但是,有时候第二个查询得到的是预期的结果,而第一个查询得到的是相同的结果。无论哪种方式,它们最终都具有相同的res值。
有什么想法,到底发生了什么,我如何才能解决这个问题?我不相信ipcMain可以实现同步方法。
渲染器中的代码(React)

// called from ipcMain when database file is set
    ipcRenderer.on('set-db', (event, arg) => {
        // Get authors
        queryDB("SELECT * FROM users;")
        .then((res) => {
            try {
                var options = [];
                res.forEach((e) => {
                    options.push({value: e.id, label: `@${e.name}`});
                    if (e.display_name != e.name) {
                        options.push({value: e.id, label: `@${e.display_name}`});
                    }
                });
                setAuthorOptions(options);
                console.log("Set author options");
            }
            catch (exception) {}
        });

        // Get channels
        queryDB("SELECT * FROM channels;")
        .then((res) => {
            try {
                var options = [];
                res.forEach((e) => {
                    if (allowedChannelTypes.includes(e.type)) {
                        options.push({value: e.id, label: `# ${e.name}`});
                    }
                });
                setChannelOptions(options);
                console.log("Set channel options");
            }
            catch (exception) {}
        });

    });

下面是主进程中的代码

ipcMain.on('asynchronous-message', (event, sql) => {
    if (db) {
        db.all(sql, (error, rows) => {
            event.reply('asynchronous-reply', (error && error.message) || rows);
        });
    }
    return
});

和渲染器代码

export default function queryDB(sql) {
    return new Promise((res) => {
        ipcRenderer.once('asynchronous-reply', (_, arg) => {
            res(arg);
        });

        ipcRenderer.send('asynchronous-message', sql);
    })
}
px9o7tmv

px9o7tmv1#

问题是您使用相同的通道名多次使用ipcRenderer.once。如文档中所述,此方法:
为事件添加一次性listener函数。这个listener只有在下一次向channel发送消息时才会被调用,之后它将被删除。
因此,如果您在前一个呼叫得到答复之前拨打新的呼叫,它们都将收到第一个被应答的呼叫的结果。
我不熟悉SQLite,但从我收集的信息来看,根据您使用的库,db.all()要么使用回调函数,要么是一个promise。我看到两种方法来解决这个问题:

With a promise

如果使用promise方式,可以简单地使用invoke/handle。例如:

渲染器

export default function queryDB(sql) {
    return ipcRenderer.invoke('query-db', sql);
}

主要

ipcMain.handle('query-db', async (event, sql) => {
  if(!db) return;

  const res = await db.all(sql);
  return res;
});

带回调

如果您更喜欢使用回调,则必须确保使用ipcRenderer.once的唯一通道名称,例如:

渲染器

export default function queryDB(sql) {
    return new Promise((res) => {
        const channelName = 'asynchronous-reply-' + Date.now();

        ipcRenderer.once(channelName, (_, arg) => {
            res(arg);
        });

        ipcRenderer.send('asynchronous-message', sql, channelName);
    })
}

主要

ipcMain.on('asynchronous-message', (event, sql, channelName) => {
    if (db) {
        db.all(sql, (error, rows) => {
            event.reply(channelName, (error && error.message) || rows);
        });
    }
    return
});

如果您使用此方法,并且由于您并不总是向创建的通道发送回复,因此还需要确保使用ipcRenderer.removeListener或ipcRenderer. removeAllListener清理未使用的侦听器。

相关问题