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)

解释

  1. 构造函数:定义了一个 AsyncWork 类,继承自 Napi::AsyncWorker。构造函数接受一个回调函数作为参数。
  2. Execute 方法:这是实际执行异步任务的地方。在这个例子中,我们简单地进行了一个耗时的计算。
  3. OnOK 方法:当异步任务完成后调用此方法,并将结果传递回 JavaScript。
  4. 导出函数DoSomethingAsync 函数用于创建并启动异步工作。如果传入的参数不是一个函数,则抛出一个错误。
  5. 初始化模块:使用 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));

说明

  1. 创建异步函数:在 AsyncWork 函数中,我们创建了一个 uv_work_t 结构体来保存数据,并将其提交给工作线程。
  2. 异步操作:在工作线程中,我们模拟了一个耗时的操作,然后通过 resolver->Resolve() 将结果传递回主线程。
  3. Promise 处理:在 Node.js 端,我们调用 addon.asyncMethod() 并使用 .then() 来处理异步结果。

这种方法允许你在 Node.js Addon 中实现异步操作,同时保持主线程的响应性。

回到顶部