
本文档旨在指导开发者如何从 NAPI (Node.js Addon API) 后端向 Electron 应用发送请求或消息。文章将介绍如何利用 Promise 和回调函数,实现 NAPI 模块与 Electron 主进程之间的通信,并提供详细的代码示例和步骤说明,帮助开发者构建更高效、更灵活的 Electron 应用。
从 NAPI 向 Electron 发送消息
核心问题是如何在 NAPI 模块中触发 Electron 主进程中的事件或函数,以便将数据从后端传递到前端。通常,NAPI 函数会返回一个值,但这并不总是满足需求,尤其是在需要异步地将数据推送到 Electron 应用时。
使用 Promise 传递数据
最直接的方式是利用 Promise。Electron 的 ipcMain.handle 函数允许你注册一个可以被渲染进程调用的异步函数。NAPI 模块可以通过返回一个 Promise,将结果传递给 Electron 主进程。
示例:
假设你有一个 NAPI 函数 SimpleFunction,它接收一个索引,并需要将处理结果发送回 Electron。
scripts.js (渲染进程):
function ClickButtonEvent(currentID) {
api.SimpleFunction(parseInt(currentID)).then((data) => {
// 处理从 NAPI 返回的数据
console.log("Received data from NAPI:", data);
});
}main.js (主进程):
const { app, BrowserWindow, Menu, ipcMain, dialog } = require('electron');
const path = require('path');
const getPcapData = require('./NAPI/build/Release/operations');
function createWindow() {
const win = new BrowserWindow({
width: 1920,
height: 1080,
minWidth: 500,
minHeight: 500,
maxWidth: 1920,
maxHeight: 1080,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
win.loadFile('src/HTML/index.html');
}
app.whenReady().then(() => {
createWindow();
ipcMain.handle('SimpleFunction', async (event, index) => {
const result = await getPcapData.SimpleFunction(index);
return result;
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});preload.js (预加载脚本):
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('api', {
SimpleFunction: async (index) => {
return await ipcRenderer.invoke('SimpleFunction', index);
}
});operations.cc (NAPI 模块):
#include#include #define NAPI_CALL(env, call) \ do \ { \ napi_status status = (call); \ if (status != napi_ok) \ { \ const napi_extended_error_info *error_info = NULL; \ napi_get_last_error_info((env), &error_info); \ bool is_pending; \ napi_is_exception_pending((env), &is_pending); \ if (!is_pending) \ { \ const char *message = (error_info->error_message == NULL) \ ? "empty error message" \ : error_info->error_message; \ napi_throw_error((env), NULL, message); \ return NULL; } \ } \ } while (0) napi_value SimpleFunction(napi_env env, napi_callback_info info) { napi_deferred deferred; napi_value promise; NAPI_CALL(env, napi_create_promise(env, &deferred, &promise)); size_t argc = 1; napi_value args[1]; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); int index = 0; NAPI_CALL(env, napi_get_value_int32(env, args[0], &index)); // 模拟异步操作 // 在实际应用中,这里会是你的后端代码 // 这里使用一个简单的定时器来模拟异步操作 std::thread([env, deferred, index]() { // 模拟一些计算 int result = index * 2; // 创建一个 NAPI 数字对象 napi_value napi_result; NAPI_CALL(env, napi_create_number(env, result, &napi_result)); // 使用 napi_resolve_deferred 来解决 Promise NAPI_CALL(env, napi_resolve_deferred(env, deferred, napi_result)); }).detach(); return promise; } napi_value init(napi_env env, napi_value exports) { napi_value simpleFunction; napi_create_function(env, nullptr, 0, SimpleFunction, nullptr, &simpleFunction); napi_set_named_property(env, exports, "SimpleFunction", simpleFunction); return exports; } NAPI_MODULE(NODE_GYP_MODULE_NAME, init);
在这个例子中,SimpleFunction 创建了一个 Promise,并在一个单独的线程中模拟了一个异步操作。操作完成后,使用 napi_resolve_deferred 来解决 Promise,并将结果传递给 Electron 主进程。
使用回调函数
另一种方法是使用回调函数。你可以在 JavaScript 中定义一个函数,并将其作为参数传递给 NAPI 函数。NAPI 函数在完成操作后,调用这个回调函数。
示例:
scripts.js (渲染进程):
function ClickButtonEvent(currentID) {
api.SimpleFunction(parseInt(currentID), (data) => {
// 处理从 NAPI 返回的数据
console.log("Received data from NAPI via callback:", data);
});
}preload.js (预加载脚本):
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('api', {
SimpleFunction: (index, callback) => ipcRenderer.invoke('SimpleFunction', index).then(callback)
});main.js (主进程):
const { app, BrowserWindow, Menu, ipcMain, dialog } = require('electron');
const path = require('path');
const getPcapData = require('./NAPI/build/Release/operations');
function createWindow() {
const win = new BrowserWindow({
width: 1920,
height: 1080,
minWidth: 500,
minHeight: 500,
maxWidth: 1920,
maxHeight: 1080,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
win.loadFile('src/HTML/index.html');
}
app.whenReady().then(() => {
createWindow();
ipcMain.handle('SimpleFunction', async (event, index) => {
const result = await getPcapData.SimpleFunction(index);
return result;
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});operations.cc (NAPI 模块):
#include#include #include #include #define NAPI_CALL(env, call) \ do \ { \ napi_status status = (call); \ if (status != napi_ok) \ { \ const napi_extended_error_info *error_info = NULL; \ napi_get_last_error_info((env), &error_info); \ bool is_pending; \ napi_is_exception_pending((env), &is_pending); \ if (!is_pending) \ { \ const char *message = (error_info->error_message == NULL) \ ? "empty error message" \ : error_info->error_message; \ napi_throw_error((env), NULL, message); \ return NULL; \ } \ } \ } while (0) napi_value SimpleFunction(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value args[1]; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); int index = 0; NAPI_CALL(env, napi_get_value_int32(env, args[0], &index)); // 模拟一些计算 int result = index * 2; return nullptr; } napi_value init(napi_env env, napi_value exports) { napi_value simpleFunction; napi_create_function(env, nullptr, 0, SimpleFunction, nullptr, &simpleFunction); napi_set_named_property(env, exports, "SimpleFunction", simpleFunction); return exports; } NAPI_MODULE(NODE_GYP_MODULE_NAME, init);
注意事项:
- 确保正确处理 NAPI 中的错误。
- 在多线程环境中使用 NAPI 时,要注意线程安全。
- 使用 Promise 或回调函数时,要确保在适当的时间释放资源。
总结
通过使用 Promise 或回调函数,可以方便地从 NAPI 模块向 Electron 应用发送消息。选择哪种方法取决于你的具体需求。Promise 更适合处理异步操作,而回调函数更适合简单的事件通知。理解这两种方法,并根据你的应用场景选择合适的方法,可以帮助你构建更高效、更灵活的 Electron 应用。










