使用Node-API接口进行异步任务开发
====================

#### 场景介绍

[napi\_create\_async\_work](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/napi#napi_create_async_work)是Node-API接口之一，用于创建一个异步工作对象。在需要执行耗时操作的场景中使用，避免阻塞env所在的ArkTS线程，确保应用程序的性能和响应速度。例如以下场景：

*   文件操作：读取大型文件或执行复杂的文件操作时，可以使用异步工作对象来避免阻塞env所在的ArkTS线程。
    
*   网络请求：当需要进行网络请求并等待响应时，使用异步工作对象确保主线程不被阻塞，提高应用程序的响应性能。
    
*   数据库操作：当需要执行复杂的数据库查询或写入操作时，使用异步工作对象确保主线程不被阻塞，提高应用程序的并发性能。
    
*   图像处理：当需要对大型图像进行处理或执行复杂的图像算法时，使用异步工作对象确保主线程不被阻塞，提高应用程序的实时性能。
    

napi\_queue\_async\_work接口使用uv\_queue\_work能力，并管理回调中napi\_value的生命周期。

异步调用支持callback和Promise两种方式，选择哪种方式由开发者决定。以下是两种方式的示例代码：

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/7a/v3/3VvDnHPTRzaCEdCn1FgL4Q/zh-cn_image_0000002634010136.png?HW-CC-KV=V1&HW-CC-Date=20260709T132816Z&HW-CC-Expire=86400&HW-CC-Sign=CCC07696788808E5FE06D8682FFB16623E2D069FC845305B03654444317E9F39)

#### 使用Promise方式示例

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/0d/v3/dii4KfkvR1CdhYMF3WmzEA/zh-cn_image_0000002664209285.png?HW-CC-KV=V1&HW-CC-Date=20260709T132816Z&HW-CC-Expire=86400&HW-CC-Sign=779684FAA4BF6EC842EA92D2AB37D8A992C50529EB1C87AA49CC1ACA12717B4E)

1.  CMakeLists.txt配置
    
    \# the minimum version of CMake.
    cmake\_minimum\_required(VERSION 3.5.0)
    project(NodeAPIAsynchronousTask)
    
    set(NATIVERENDER\_ROOT\_PATH ${CMAKE\_CURRENT\_SOURCE\_DIR})
    
    if(DEFINED PACKAGE\_FIND\_FILE)
        include(${PACKAGE\_FIND\_FILE})
    endif()
    
    include\_directories(${NATIVERENDER\_ROOT\_PATH}
                        ${NATIVERENDER\_ROOT\_PATH}/include)
    
    add\_library(entry SHARED napi\_init.cpp)
    target\_link\_libraries(entry PUBLIC libace\_napi.z.so)
    
    add\_library(entry1 SHARED callback.cpp)
    target\_link\_libraries(entry1 PUBLIC libace\_napi.z.so)
    
2.  使用napi\_create\_async\_work创建异步任务，使用napi\_queue\_async\_work将任务加入队列，等待执行。
    
    #include "napi/native\_api.h"
    // 调用方提供的data context，该数据会传递给execute和complete函数
    struct CallbackData {
        napi\_async\_work asyncWork = nullptr;
        napi\_deferred deferred = nullptr;
        napi\_ref callback = nullptr;
        double args = 0;
        double result = 0;
    };
    
    // ...
    
    static napi\_value AsyncWork(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);
    
        napi\_value promise = nullptr;
        napi\_deferred deferred = nullptr;
        napi\_create\_promise(env, &deferred, &promise);
    
        auto callbackData = new CallbackData();
        callbackData->deferred = deferred;
        napi\_get\_value\_double(env, args\[0\], &callbackData->args);
    
        napi\_value resourceName = nullptr;
        napi\_create\_string\_utf8(env, "AsyncCallback", NAPI\_AUTO\_LENGTH, &resourceName);
        // 创建异步任务
        napi\_create\_async\_work(env, nullptr, resourceName, ExecuteCB, CompleteCB, callbackData, &callbackData->asyncWork);
        // 将异步任务加入队列
        napi\_queue\_async\_work(env, callbackData->asyncWork);
    
        return promise;
    }
    
3.  定义异步任务的第一个回调函数，该函数在工作线程中执行，处理具体的业务逻辑。
    
    static void ExecuteCB(napi\_env env, void \*data)
    {
        CallbackData \*callbackData = reinterpret\_cast<CallbackData \*>(data);
        callbackData->result = callbackData->args;
    }
    
4.  定义异步任务的第二个回调函数，该函数在主线程执行，将结果传递给ArkTS侧。
    
    static void CompleteCB(napi\_env env, napi\_status status, void \*data)
    {
        CallbackData \*callbackData = reinterpret\_cast<CallbackData \*>(data);
        napi\_value result = nullptr;
        napi\_create\_double(env, callbackData->result, &result);
        if (callbackData->result > 0) {
            napi\_resolve\_deferred(env, callbackData->deferred, result);
        } else {
            napi\_reject\_deferred(env, callbackData->deferred, result);
        }
    
        napi\_delete\_async\_work(env, callbackData->asyncWork);
        delete callbackData;
        callbackData = nullptr;
    }
    
5.  模块注册及ArkTS侧调用接口。
    
    // 模块初始化
    static napi\_value Init(napi\_env env, napi\_value exports)
    {
        napi\_property\_descriptor desc\[\] = {
            { "asyncWork", nullptr, AsyncWork, nullptr, nullptr, nullptr, napi\_default, nullptr }
        };
        napi\_define\_properties(env, exports, sizeof(desc) / sizeof(desc\[0\]), desc);
        return exports;
    }
    
    接口对应的.d.ts描述。
    
    // index.d.ts
    export const asyncWork: (data: number) => Promise<number>;
    
    ArkTS侧调用接口。
    
    import { hilog } from '@kit.PerformanceAnalysisKit';
    import testNapi from 'libentry.so';
    
    testNapi.asyncWork(1024).then((result: number) => {
      hilog.info(0x0000, 'XXX', 'result is %{public}d', result);
    });
    
    运行结果：result is 1024
    

#### 使用callback方式示例

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/43/v3/kf3AIvICTLKfa_-0nzpmKA/zh-cn_image_0000002664329345.png?HW-CC-KV=V1&HW-CC-Date=20260709T132816Z&HW-CC-Expire=86400&HW-CC-Sign=C72696C561BE6385EB03C4B470A785CCE8DB1F86E54F7F5643958BDAFDA16353)

1.  使用napi\_create\_async\_work创建异步任务，并使用napi\_queue\_async\_work将异步任务加入队列，等待执行。
    
    #include "napi/native\_api.h"
    
    static constexpr int INT\_ARGS\_2 = 2; // 入参索引
    
    // 调用方提供的data context，该数据会传递给execute和complete函数
    struct CallbackData {
        napi\_async\_work asyncWork = nullptr;
        napi\_ref callbackRef = nullptr;
        double args\[2\] = {0};
        double result = 0;
    };
    
    // ...
    napi\_value AsyncWork(napi\_env env, napi\_callback\_info info)
    {
        size\_t argc = 3;
        napi\_value args\[3\];
        napi\_get\_cb\_info(env, info, &argc, args, nullptr, nullptr);
        auto asyncContext = new CallbackData();
        // 将接收到的参数保存到callbackData
        napi\_get\_value\_double(env, args\[0\], &asyncContext->args\[0\]);
        napi\_get\_value\_double(env, args\[1\], &asyncContext->args\[1\]);
        // 将传入的callback转换为napi\_ref延长其生命周期，防止被GC掉
        napi\_create\_reference(env, args\[INT\_ARGS\_2\], 1, &asyncContext->callbackRef);
        napi\_value resourceName = nullptr;
        napi\_create\_string\_utf8(env, "asyncWorkCallback", NAPI\_AUTO\_LENGTH, &resourceName);
        // 创建异步任务
        napi\_create\_async\_work(env, nullptr, resourceName, ExecuteCB, CompleteCB,
                               asyncContext, &asyncContext->asyncWork);
        // 将异步任务加入队列
        napi\_queue\_async\_work(env, asyncContext->asyncWork);
        return nullptr;
    }
    
2.  定义异步任务的第一个回调函数，该函数在工作线程中执行，处理具体的业务逻辑。
    
    static void ExecuteCB(napi\_env env, void \*data)
    {
        CallbackData \*callbackData = reinterpret\_cast<CallbackData \*>(data);
        callbackData->result = callbackData->args\[0\] + callbackData->args\[1\];
    }
    
3.  定义异步任务的第二个回调函数，该函数在主线程执行，将结果传递给ArkTS侧。
    
    static void CompleteCB(napi\_env env, napi\_status status, void \*data)
    {
        CallbackData \*callbackData = reinterpret\_cast<CallbackData \*>(data);
        napi\_value callbackArg\[1\] = {nullptr};
        napi\_create\_double(env, callbackData->result, &callbackArg\[0\]);
        napi\_value callback = nullptr;
        napi\_get\_reference\_value(env, callbackData->callbackRef, &callback);
        // 执行回调函数
        napi\_value result;
        napi\_value undefined;
        napi\_get\_undefined(env, &undefined);
        napi\_call\_function(env, undefined, callback, 1, callbackArg, &result);
        // 删除napi\_ref对象以及异步任务
        napi\_delete\_reference(env, callbackData->callbackRef);
        napi\_delete\_async\_work(env, callbackData->asyncWork);
        delete callbackData;
        callbackData = nullptr;
    }
    
4.  模块注册以及ArkTS侧调用接口。
    
    导出方法名与上面一致，可直接复用模块注册的代码。
    
    // 模块初始化
    static napi\_value Init(napi\_env env, napi\_value exports)
    {
        napi\_property\_descriptor desc\[\] = {
            { "asyncWork", nullptr, AsyncWork, nullptr, nullptr, nullptr, napi\_default, nullptr }
        };
        napi\_define\_properties(env, exports, sizeof(desc) / sizeof(desc\[0\]), desc);
        return exports;
    }
    
    接口对应的.d.ts描述。
    
    export const asyncWork: (arg1: number, arg2: number, callback: (result: number) => void) => void;
    
    ArkTS侧调用接口。
    
    import { hilog } from '@kit.PerformanceAnalysisKit';
    import nativeModule from 'libentry1.so';
    
    let num1: number = 123;
    let num2: number = 456;
    
    nativeModule.asyncWork(num1, num2, (result: number) => {
      hilog.info(0x0000, 'XXX', 'result is %{public}d', result);
    });
    
    运行结果：result is 579
    

#### 子线程交互场景介绍

*   由于napi\_queue\_async\_work接口本身会创建一个C++子线程，因此native侧代码可以直接复用上面使用callback方式的代码，以下展示ArkTS侧使用上的差异。

#### \[h2\]基于[Worker](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/worker-introduction)实现的C++子线程与ArkTS子线程交互场景

*   DevEco Studio支持一键生成Worker，在对应的{moduleName}目录下任意位置，点击鼠标右键 > New > Worker，即可自动生成Worker的模板文件及配置信息。本文以创建 "Worker" 为例。

1.  Worker配置。
    
    "buildOption": {
      "sourceOption": {
        "workers": \[
          "./src/main/ets/workers/Worker.ets"
         \]
      },
    }
    
2.  Worker线程示例代码。
    
    // entry/src/main/ets/workers/Worker.ets
    
    import nativeModule from 'libentry1.so';
    import { worker, MessageEvents } from '@kit.ArkTS';
    
    const port = worker.workerPort;
    
    port.onmessage = (e : MessageEvents) => {
        console.info('Worker thread received data:', e.data.num1 + '、' + e.data.num2);
        nativeModule.asyncWork(e.data.num1, e.data.num2, (result: number) => {
            port.postMessage(result);
        });
    }
    
3.  ArkTS线程代码。
    
    import { hilog } from '@kit.PerformanceAnalysisKit';
    import { worker } from '@kit.ArkTS';
    let num1: number = 123;
    let num2: number = 456;
    
    const wk = new worker.ThreadWorker('entry/ets/workers/Worker.ets');
    wk.postMessage({num1, num2});
    wk.onmessage = (msg) => {
      console.info('result is:', msg.data);
      wk.terminate();
    }
    
    运行结果：
    Worker thread received data: 123、456
    result is 579
    

#### \[h2\]基于[Taskpool](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/taskpool-introduction)实现的C++子线程与ArkTS子线程交互场景

1.  ArkTS线程代码。
    
    import { hilog } from '@kit.PerformanceAnalysisKit';
    import { taskpool } from '@kit.ArkTS';
    import nativeModule from 'libentry1.so';
    let num1: number = 123;
    let num2: number = 456;
    
    @Concurrent
    function nativeCall(num1 : number, num2 : number): void {
      console.info('Taskpool thread received data:', + num1 + '、' + num2);
      nativeModule.asyncWork(num1, num2, (result: number) => {
        hilog.info(0x0000, 'XXX', 'result is: %{public}d', result);
      });
    }
    
    async function testTaskpool() : Promise<void> {
      try {
        const task = new taskpool.Task(nativeCall, num1, num2);
        await taskpool.execute(task);
      } catch (e) {
        console.error(\`Taskpool execute error: ${e}\`);
      }
    }
    
    testTaskpool();
    
    运行结果：
    Taskpool thread received data: 123、456
    result is 579
    

#### 注意事项

*   调用napi\_cancel\_async\_work接口，无论底层uv是否失败都会返回napi\_ok。若因为底层uv导致取消任务失败，complete callback中的status会传入对应错误值，请在complete callback中对status进行处理。
*   NAPI的异步工作项（napi\_async\_work）建议单次使用。napi\_queue\_async\_work后，该napi\_async\_work需在complete回调执行时或执行后，通过napi\_delete\_async\_work完成释放。同一个napi\_async\_work只允许释放一次，重复释放会导致未定义行为。
*   napi\_async\_work的execute\_cb运行在一个独立的工作线程中，该线程从uv线程池中取出。不同工作线程之间互不影响。execute\_cb函数中的业务逻辑是在工作线程中执行的，而非原始的ArkTS线程，因此不能使用入参env构造napi\_value(入参env是原始ArkTS线程的env)。
*   在任务的执行时序上，napi\_async\_work仅保证complete\_cb在execute\_cb之后执行。不同napi\_async\_work的execute\_cb在各自的工作线程上运行，因此无法保证不同execute\_cb的执行顺序。如果任务执行需要顺序，建议使用napi\_threadsafe\_function系列接口，这些接口是保序的。具体使用方法可参考[链接](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/use-napi-thread-safety)。