很好!既然你已经掌握了基础的 Node-API 使用,接下来我们继续深入,学习更高级的功能。我会分步骤带你掌握 异步操作 、线程安全 和 高性能 Buffer 处理,这些都是实际开发中常用的技术。
1. 异步操作(Async Work)
目标:让 C++ 执行耗时计算(如 1 秒),然后返回结果给 JS,不阻塞 JS 主线程。
**修改 hello.cc
**
scss
#include <thread> // 用于 sleep
#include <chrono>
// 1. 定义异步任务的数据结构
struct AsyncData {
napi_async_work work; // 异步任务
napi_deferred deferred; // 用于 Promise 的 resolve/reject
napi_env env; // 当前环境
double input; // JS 传入的参数
double result; // 计算结果
};
// 2. 执行耗时计算的函数(在子线程运行)
void ExecuteWork(napi_env env, void* data) {
AsyncData* async_data = static_cast<AsyncData*>(data);
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟耗时操作
async_data->result = async_data->input * 2; // 计算
}
// 3. 计算完成后的回调(回到 JS 线程)
void CompleteWork(napi_env env, napi_status status, void* data) {
AsyncData* async_data = static_cast<AsyncData*>(data);
// 创建返回结果
napi_value result;
napi_create_double(env, async_data->result, &result);
// 通知 JS Promise 已完成
napi_resolve_deferred(env, async_data->deferred, result);
// 清理资源
napi_delete_async_work(env, async_data->work);
delete async_data;
}
// 4. JS 调用的入口函数
napi_value AsyncCompute(napi_env env, napi_callback_info info) {
// 获取 JS 传入的参数
size_t argc = 1;
napi_value args[1];
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
double input;
napi_get_value_double(env, args[0], &input);
// 创建 Promise 和异步数据
napi_value promise;
AsyncData* async_data = new AsyncData();
async_data->env = env;
async_data->input = input;
napi_create_promise(env, &async_data->deferred, &promise);
// 创建异步任务
napi_value resource_name;
napi_create_string_utf8(env, "AsyncCompute", NAPI_AUTO_LENGTH, &resource_name);
napi_create_async_work(env, nullptr, resource_name,
ExecuteWork, CompleteWork,
async_data, &async_data->work);
napi_queue_async_work(env, async_data->work);
return promise; // 返回 Promise 给 JS
}
// 5. 在 Init 里注册 AsyncCompute
napi_value Init(napi_env env, napi_value exports) {
// ...(之前的代码)
napi_create_function(env, nullptr, 0, AsyncCompute, nullptr, &fn);
napi_set_named_property(env, exports, "asyncCompute", fn);
return exports;
}
**修改 index.js
**
javascript
console.log("开始计算...");
addon.asyncCompute(10).then(result => {
console.log("计算结果:", result); // 2 秒后输出 20
});
console.log("JS 主线程未被阻塞!");
运行
node-gyp rebuild
node index.js
你会看到:
erlang
开始计算...
JS 主线程未被阻塞!
(1 秒后)计算结果: 20
关键点
ExecuteWork
在子线程运行,不会阻塞 JS。CompleteWork
回到 JS 线程,可以安全调用 Node-API。- 使用
Promise
让 JS 可以await
或.then()
获取结果。
2. 线程安全(Thread-safe Function)
目标:在 C++ 子线程中,每隔 1 秒向 JS 发送进度信息。
**修改 hello.cc
**
scss
// 1. 定义线程安全回调的数据结构
struct ProgressData {
napi_threadsafe_function tsfn; // 线程安全函数
int progress; // 当前进度
};
// 2. 子线程的工作函数
void BackgroundThread(ProgressData* data) {
for (int i = 0; i <= 100; i += 10) {
data->progress = i;
// 调用 JS 回调(非阻塞方式)
napi_call_threadsafe_function(
data->tsfn,
nullptr, // 无额外数据
napi_tsfn_nonblocking
);
std::this_thread::sleep_for(std::chrono::seconds(1));
}
// 结束线程安全函数
napi_release_threadsafe_function(data->tsfn, napi_tsfn_release);
delete data;
}
// 3. JS 调用的入口函数
napi_value StartProgress(napi_env env, napi_callback_info info) {
// 获取 JS 回调函数
size_t argc = 1;
napi_value args[1];
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// 创建线程安全函数
ProgressData* data = new ProgressData();
napi_create_threadsafe_function(
env,
args[0], // JS 回调函数
nullptr, // 无 this 绑定
napi_create_string_utf8(env, "Progress", NAPI_AUTO_LENGTH),
0, 1, nullptr,
[](napi_env env, void* finalize_data, void* hint) {}, // 清理函数
&data->tsfn
);
// 启动子线程
std::thread(BackgroundThread, data).detach();
return nullptr;
}
// 4. 在 Init 里注册 StartProgress
napi_value Init(napi_env env, napi_value exports) {
// ...(之前的代码)
napi_create_function(env, nullptr, 0, StartProgress, nullptr, &fn);
napi_set_named_property(env, exports, "startProgress", fn);
return exports;
}
**修改 index.js
**
javascript
addon.startProgress((progress) => {
console.log("当前进度:", progress);
});
运行
node-gyp rebuild
node index.js
你会看到每隔 1 秒输出:
makefile
当前进度: 0
当前进度: 10
...
当前进度: 100
关键点
napi_threadsafe_function
允许子线程安全调用 JS 函数。napi_call_threadsafe_function
是非阻塞的,适合高频回调。- 结束时必须
napi_release_threadsafe_function
。
3. 高性能 Buffer 处理
目标 :在 C++ 中直接操作 JS 的 ArrayBuffer
,避免数据拷贝。
**修改 hello.cc
**
scss
// 1. 处理 ArrayBuffer 的函数
napi_value ProcessBuffer(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// 检查是否为 ArrayBuffer
bool is_arraybuffer;
napi_is_arraybuffer(env, args[0], &is_arraybuffer);
if (!is_arraybuffer) {
napi_throw_error(env, nullptr, "参数必须是 ArrayBuffer");
return nullptr;
}
// 获取 ArrayBuffer 的数据指针
void* data;
size_t length;
napi_get_arraybuffer_info(env, args[0], &data, &length);
// 直接修改数据(无需拷贝)
uint8_t* bytes = static_cast<uint8_t*>(data);
for (size_t i = 0; i < length; i++) {
bytes[i] += 1; // 每个字节 +1
}
return args[0]; // 返回修改后的 ArrayBuffer
}
// 2. 在 Init 里注册 ProcessBuffer
napi_value Init(napi_env env, napi_value exports) {
// ...(之前的代码)
napi_create_function(env, nullptr, 0, ProcessBuffer, nullptr, &fn);
napi_set_named_property(env, exports, "processBuffer", fn);
return exports;
}
**修改 index.js
**
ini
const buffer = new ArrayBuffer(4);
const view = new Uint8Array(buffer);
view[0] = 1;
view[1] = 2;
view[2] = 3;
view[3] = 4;
addon.processBuffer(buffer);
console.log(view); // 输出: Uint8Array [ 2, 3, 4, 5 ]
运行
node-gyp rebuild
node index.js
你会看到 Uint8Array
的值被 C++ 直接修改了。
关键点
napi_get_arraybuffer_info
获取原始指针,避免数据拷贝。- 直接操作内存比
napi_set_element
快 100 倍以上。 - 适用于图像处理、音视频编解码等高性能场景。
总结
技术 | 适用场景 | 关键 API |
---|---|---|
异步操作 | 耗时计算(文件 I/O、网络请求) | napi_create_async_work , napi_queue_async_work |
线程安全 | 子线程实时通知 JS(如进度更新) | napi_threadsafe_function , napi_call_threadsafe_function |
Buffer 处理 | 高性能数据操作(图像/音频处理) | napi_get_arraybuffer_info , 直接内存操作 |
下一步建议
- 尝试用
asyncCompute
实现一个文件哈希计算器。 - 用
startProgress
做一个真实的进度条(如文件下载)。 - 用
processBuffer
实现一个简单的图像滤镜(如反色)。
如果有任何问题,欢迎随时问我! 😊