Nodejs Addon 如何实现异步呢?

Nodejs Addon 如何实现异步呢?

如题

2 回复

Node.js Addon 如何实现异步呢?

在 Node.js 中,Addon 是用 C++ 编写的扩展模块,用于提高性能或访问底层系统功能。如果你需要在 Node.js Addon 中实现异步操作,可以使用 libuv 的异步处理机制。libuv 是一个跨平台的异步 I/O 库,Node.js 使用它来处理异步操作。

实现步骤

  1. 定义异步函数:创建一个异步函数,该函数将任务提交给 libuv 线程池,并在完成后调用回调函数。
  2. 创建回调函数:创建一个回调函数,该函数将在异步操作完成后被调用。
  3. 暴露给 JavaScript:将这个异步函数暴露给 JavaScript 侧,以便可以在 Node.js 中调用它。

示例代码

假设我们有一个简单的异步任务,比如计算一个数组的总和。我们将使用 Node.js Addon 来实现这个功能。

C++ 代码(addon.cpp)
#include <node_api.h>
#include <vector>

// 回调函数
void SumAsyncCompleteCallback(napi_env env, napi_status status, void* data) {
    auto sum = static_cast<int*>(data);
    napi_value js_sum;
    napi_create_int32(env, *sum, &js_sum);
    napi_value js_callback;
    napi_get_reference_value(env, (napi_ref)data, &js_callback);
    napi_value undefined;
    napi_get_undefined(env, &undefined);
    napi_value args[] = { undefined, js_sum };
    napi_call_function(env, undefined, js_callback, 2, args, nullptr);
    napi_delete_reference(env, (napi_ref)data);
}

// 异步执行函数
napi_value SumAsync(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value argv[1];
    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);

    std::vector<int> numbers;
    napi_valuetype valuetype;
    napi_typeof(env, argv[0], &valuetype);
    if (valuetype == napi_object) {
        napi_value array;
        napi_get_named_property(env, argv[0], "numbers", &array);
        size_t len;
        napi_get_array_length(env, array, &len);
        for (size_t i = 0; i < len; ++i) {
            napi_value element;
            napi_get_element(env, array, i, &element);
            int num;
            napi_get_value_int32(env, element, &num);
            numbers.push_back(num);
        }
    }

    napi_value async_resource;
    napi_create_string_utf8(env, "SumAsync", NAPI_AUTO_LENGTH, &async_resource);

    napi_value promise;
    napi_value deferred;
    napi_deferred_create(env, &deferred, &promise);
    napi_create_reference(env, argv[0], 1, &async_resource);

    napi_async_work work;
    napi_create_async_work(
        env,
        async_resource,
        "SumAsync",
        [](napi_env env, void* data) {},
        [](napi_env env, napi_status status, void* data) {
            auto sum = static_cast<int*>(data);
            delete sum;
        },
        new int(numbers.size()),
        &work
    );

    napi_queue_async_work(env, work);

    return promise;
}

// 初始化函数
napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor descriptors[] = {
        { "sumAsync", nullptr, SumAsync, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    napi_define_properties(env, exports, 1, descriptors);
    return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
JavaScript 代码(index.js)
const addon = require('./build/Release/addon');

const numbers = [1, 2, 3, 4, 5];

addon.sumAsync({ numbers })
    .then(sum => console.log(`Sum: ${sum}`))
    .catch(err => console.error(err));

在这个例子中,我们定义了一个 SumAsync 函数,该函数接受一个包含数字的数组,并将其提交给 libuv 的线程池进行异步计算。当计算完成后,回调函数将结果返回给 JavaScript 代码。


在 Node.js Addon 中实现异步操作通常需要使用多线程或者回调机制。这里介绍一种常见的方法:使用 libuv 的工作线程池来处理异步操作,并通过回调函数返回结果。

以下是一个简单的示例代码,展示如何创建一个 Node.js Addon 来实现异步功能:

1. 编写 C++ 代码

首先,你需要安装 node-gyp 工具来编译 C++ 代码到 Node.js Addon。你可以使用 npm 安装它:

npm install -g node-gyp

接下来,创建一个文件 addon.cc 并添加以下代码:

#include <node.h>
#include <v8.h>

using namespace v8;

void AsyncWork(const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = Isolate::GetCurrent();
    HandleScope scope(isolate);

    // 获取参数
    Local<String> input = args[0].As<String>();

    // 创建一个工作对象
    uv_work_t* work = new uv_work_t;
    work->data = new std::string(*String::Utf8Value(input));

    // 调用异步执行函数
    uv_queue_work(uv_default_loop(), work, AsyncExecute, (uv_after_work_cb)AsyncAfter);
}

// 异步执行函数
void AsyncExecute(uv_work_t* req) {
    std::string* input = static_cast<std::string*>(req->data);

    // 这里模拟一些耗时的操作
    for (int i = 0; i < 100000000; i++) {}

    delete input;
}

// 异步完成后的回调
void AsyncAfter(uv_work_t* req, int status) {
    Isolate* isolate = Isolate::GetCurrent();
    HandleScope scope(isolate);

    // 返回结果
    Local<Value> argv[] = { String::NewFromUtf8(isolate, "Done!") };
    Isolate::GetCurrent()->GetCurrentContext()->Global()
        ->GetPrototype().ToObject()
        ->GetConstructor()
        ->CallAsFunction(Context::GetCurrent()->Global(), 1, argv);
}

void Init(Handle<Object> exports) {
    NODE_SET_METHOD(exports, "asyncFunction", AsyncWork);
}

NODE_MODULE(addon, Init)

2. 编译并加载 Addon

在项目根目录下创建一个 binding.gyp 文件,并添加以下内容:

{
  "targets": [
    {
      "target_name": "addon",
      "sources": [ "addon.cc" ]
    }
  ]
}

然后在命令行中运行以下命令来编译 Addon:

node-gyp configure build

编译完成后,你会得到一个 build/Release/addon.node 文件。接下来,你可以编写一个 Node.js 脚本来加载和测试这个 Addon:

const addon = require('./build/Release/addon');

addon.asyncFunction('Hello World', (result) => {
    console.log(result); // 输出 'Done!'
});

这样,你就实现了一个简单的异步 Node.js Addon。

回到顶部