Nodejs Addon 如何实现异步呢?
Nodejs Addon 如何实现异步呢?
如题
3 回复
Node.js Addon 如何实现异步?
在 Node.js 中,编写 C++ 扩展(即 Node.js Addon)时,实现异步操作是一个常见的需求。通常情况下,我们需要在 Node.js Addon 中处理一些耗时的任务,例如文件 I/O、网络请求或复杂的计算任务。为了不阻塞事件循环,这些操作需要以异步的方式执行。
使用 Nan::AsyncWorker
实现异步
Nan::AsyncWorker
是一个非常方便的工具,它可以帮助你轻松地创建异步操作。下面是一个简单的示例,展示了如何使用 Nan::AsyncWorker
来实现异步功能。
首先,确保你的项目中已经安装了 node-addon-api
,因为 Nan
已经被废弃,建议使用 node-addon-api
。
#include <napi.h>
class AsyncWork : public Napi::AsyncWorker {
public:
// 构造函数
AsyncWork(Napi::Function& callback)
: Napi::AsyncWorker(callback) {}
// 执行实际的工作
void Execute() override {
// 这里模拟一个耗时的操作
int result = 0;
for (int i = 0; i < 1000000000; ++i) {
result += i;
}
// 将结果存储在成员变量中
this->result = result;
}
// 操作完成后的回调
void OnOK() override {
Napi::Env env = Env();
// 返回结果给 JavaScript
Napi::HandleScope scope(env);
Callback().Call({Env().Undefined(), Napi::Number::New(env, result)});
}
private:
int result;
};
// 导出一个异步函数
Napi::Value DoSomethingAsync(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (info.Length() < 1 || !info[0].IsFunction()) {
Napi::TypeError::New(env, "Expected a callback").ThrowAsJavaScriptException();
return env.Undefined();
}
// 创建并启动异步工作
auto* asyncWork = new AsyncWork(info[0].As<Napi::Function>());
asyncWork->Queue();
return env.Undefined();
}
// 初始化模块
NAPI_MODULE(NODE_GYP_MODULE_NAME, DoSomethingAsync)
解释
- 构造函数:定义了一个
AsyncWork
类,继承自Napi::AsyncWorker
。构造函数接受一个回调函数作为参数。 - Execute 方法:这是实际执行异步任务的地方。在这个例子中,我们简单地进行了一个耗时的计算。
- OnOK 方法:当异步任务完成后调用此方法,并将结果传递回 JavaScript。
- 导出函数:
DoSomethingAsync
函数用于创建并启动异步工作。如果传入的参数不是一个函数,则抛出一个错误。 - 初始化模块:使用
NAPI_MODULE
宏来导出模块中的函数。
通过这种方式,你可以在 Node.js Addon 中实现异步操作,从而避免阻塞 Node.js 的事件循环。
要在 Node.js Addon 中实现异步操作,可以使用 Libuv 的异步处理机制。通常我们会使用工作线程(worker thread)来执行耗时的操作,并通过回调、事件循环或 Promise 来通知主线程。
以下是一个简单的例子,展示了如何在 Node.js Addon 中实现异步操作:
C++ 部分(addon.cpp)
#include <node.h>
#include <v8.h>
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Value;
using v8::Promise;
void AsyncWork(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
// 创建一个 Promise
Local<Promise::Resolver> resolver = Promise::Resolver::New(isolate->GetCurrentContext()).ToLocalChecked();
// 在后台线程中执行异步操作
uv_work_t* work = new uv_work_t;
work->data = new Persistent<Promise::Resolver>(isolate, resolver);
uv_queue_work(uv_default_loop(), work, [](uv_work_t* work) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
Persistent<Promise::Resolver>* resolver_persistent = reinterpret_cast<Persistent<Promise::Resolver>*>(work->data);
Isolate::Scope isolate_scope(isolate);
TryCatch try_catch(isolate);
// 模拟异步操作
int result = 0;
for (int i = 0; i < 100000000; ++i) {
result += i;
}
// 解析 Promise
Local<Value> argv[] = { Number::New(isolate, result) };
resolver_persistent->Get(isolate)->Resolve(isolate->GetCurrentContext(), Array::New(isolate, argv)).ToChecked();
resolver_persistent->Reset(); // 释放引用
delete resolver_persistent;
}, [](uv_work_t* req, int status) {
delete req;
});
args.GetReturnValue().Set(resolver->GetPromise());
}
void Initialize(Local<Object> exports) {
NODE_SET_METHOD(exports, "asyncMethod", AsyncWork);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
Node.js 部分(index.js)
const addon = require('./build/Release/addon');
addon.asyncMethod()
.then(result => console.log('Async result:', result))
.catch(err => console.error('Error:', err));
说明
- 创建异步函数:在
AsyncWork
函数中,我们创建了一个uv_work_t
结构体来保存数据,并将其提交给工作线程。 - 异步操作:在工作线程中,我们模拟了一个耗时的操作,然后通过
resolver->Resolve()
将结果传递回主线程。 - Promise 处理:在 Node.js 端,我们调用
addon.asyncMethod()
并使用.then()
来处理异步结果。
这种方法允许你在 Node.js Addon 中实现异步操作,同时保持主线程的响应性。