文档管理中心

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

场景介绍

napi_create_async_work是Node-API接口之一,用于创建一个异步工作对象。在需要执行耗时操作的场景中使用,避免阻塞env所在的ArkTS线程,确保应用程序的性能和响应速度。例如以下场景:

  • 文件操作:读取大型文件或执行复杂的文件操作时,可以使用异步工作对象来避免阻塞env所在的ArkTS线程。

  • 网络请求:当需要进行网络请求并等待响应时,使用异步工作对象确保主线程不被阻塞,提高应用程序的响应性能。

  • 数据库操作:当需要执行复杂的数据库查询或写入操作时,使用异步工作对象确保主线程不被阻塞,提高应用程序的并发性能。

  • 图像处理:当需要对大型图像进行处理或执行复杂的图像算法时,使用异步工作对象确保主线程不被阻塞,提高应用程序的实时性能。

napi_queue_async_work接口使用uv_queue_work能力,并管理回调中napi_value的生命周期。

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

使用Promise方式示例

  1. CMakeLists.txt配置

    收起
    自动换行
    深色代码主题
    复制
    1. # the minimum version of CMake.
    2. cmake_minimum_required(VERSION 3.5.0)
    3. project(NodeAPIAsynchronousTask)
    4. set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
    5. if(DEFINED PACKAGE_FIND_FILE)
    6. include(${PACKAGE_FIND_FILE})
    7. endif()
    8. include_directories(${NATIVERENDER_ROOT_PATH}
    9. ${NATIVERENDER_ROOT_PATH}/include)
    10. add_library(entry SHARED napi_init.cpp)
    11. target_link_libraries(entry PUBLIC libace_napi.z.so)
    12. add_library(entry1 SHARED callback.cpp)
    13. target_link_libraries(entry1 PUBLIC libace_napi.z.so)
  2. 使用napi_create_async_work创建异步任务,使用napi_queue_async_work将任务加入队列,等待执行。

    收起
    自动换行
    深色代码主题
    复制
    1. #include "napi/native_api.h"
    2. // 调用方提供的data context,该数据会传递给execute和complete函数
    3. struct CallbackData {
    4. napi_async_work asyncWork = nullptr;
    5. napi_deferred deferred = nullptr;
    6. napi_ref callback = nullptr;
    7. double args = 0;
    8. double result = 0;
    9. };
    10. // ...
    11. static napi_value AsyncWork(napi_env env, napi_callback_info info)
    12. {
    13. size_t argc = 1;
    14. napi_value args[1];
    15. napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    16. napi_value promise = nullptr;
    17. napi_deferred deferred = nullptr;
    18. napi_create_promise(env, &deferred, &promise);
    19. auto callbackData = new CallbackData();
    20. callbackData->deferred = deferred;
    21. napi_get_value_double(env, args[0], &callbackData->args);
    22. napi_value resourceName = nullptr;
    23. napi_create_string_utf8(env, "AsyncCallback", NAPI_AUTO_LENGTH, &resourceName);
    24. // 创建异步任务
    25. napi_create_async_work(env, nullptr, resourceName, ExecuteCB, CompleteCB, callbackData, &callbackData->asyncWork);
    26. // 将异步任务加入队列
    27. napi_queue_async_work(env, callbackData->asyncWork);
    28. return promise;
    29. }
  3. 定义异步任务的第一个回调函数,该函数在工作线程中执行,处理具体的业务逻辑。

    收起
    自动换行
    深色代码主题
    复制
    1. static void ExecuteCB(napi_env env, void *data)
    2. {
    3. CallbackData *callbackData = reinterpret_cast<CallbackData *>(data);
    4. callbackData->result = callbackData->args;
    5. }
  4. 定义异步任务的第二个回调函数,该函数在主线程执行,将结果传递给ArkTS侧。

    收起
    自动换行
    深色代码主题
    复制
    1. static void CompleteCB(napi_env env, napi_status status, void *data)
    2. {
    3. CallbackData *callbackData = reinterpret_cast<CallbackData *>(data);
    4. napi_value result = nullptr;
    5. napi_create_double(env, callbackData->result, &result);
    6. if (callbackData->result > 0) {
    7. napi_resolve_deferred(env, callbackData->deferred, result);
    8. } else {
    9. napi_reject_deferred(env, callbackData->deferred, result);
    10. }
    11. napi_delete_async_work(env, callbackData->asyncWork);
    12. delete callbackData;
    13. callbackData = nullptr;
    14. }
  5. 模块注册及ArkTS侧调用接口。

    收起
    自动换行
    深色代码主题
    复制
    1. // 模块初始化
    2. static napi_value Init(napi_env env, napi_value exports)
    3. {
    4. napi_property_descriptor desc[] = {
    5. { "asyncWork", nullptr, AsyncWork, nullptr, nullptr, nullptr, napi_default, nullptr }
    6. };
    7. napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    8. return exports;
    9. }

    接口对应的.d.ts描述。

    收起
    自动换行
    深色代码主题
    复制
    1. // index.d.ts
    2. export const asyncWork: (data: number) => Promise<number>;

    ArkTS侧调用接口。

    收起
    自动换行
    深色代码主题
    复制
    1. import { hilog } from '@kit.PerformanceAnalysisKit';
    2. import testNapi from 'libentry.so';
    收起
    自动换行
    深色代码主题
    复制
    1. testNapi.asyncWork(1024).then((result: number) => {
    2. hilog.info(0x0000, 'XXX', 'result is %{public}d', result);
    3. });
    收起
    自动换行
    深色代码主题
    复制
    1. 运行结果:result is 1024

使用callback方式示例

  1. 使用napi_create_async_work创建异步任务,并使用napi_queue_async_work将异步任务加入队列,等待执行。

    收起
    自动换行
    深色代码主题
    复制
    1. #include "napi/native_api.h"
    2. static constexpr int INT_ARGS_2 = 2; // 入参索引
    3. // 调用方提供的data context,该数据会传递给execute和complete函数
    4. struct CallbackData {
    5. napi_async_work asyncWork = nullptr;
    6. napi_ref callbackRef = nullptr;
    7. double args[2] = {0};
    8. double result = 0;
    9. };
    10. // ...
    11. napi_value AsyncWork(napi_env env, napi_callback_info info)
    12. {
    13. size_t argc = 3;
    14. napi_value args[3];
    15. napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    16. auto asyncContext = new CallbackData();
    17. // 将接收到的参数保存到callbackData
    18. napi_get_value_double(env, args[0], &asyncContext->args[0]);
    19. napi_get_value_double(env, args[1], &asyncContext->args[1]);
    20. // 将传入的callback转换为napi_ref延长其生命周期,防止被GC掉
    21. napi_create_reference(env, args[INT_ARGS_2], 1, &asyncContext->callbackRef);
    22. napi_value resourceName = nullptr;
    23. napi_create_string_utf8(env, "asyncWorkCallback", NAPI_AUTO_LENGTH, &resourceName);
    24. // 创建异步任务
    25. napi_create_async_work(env, nullptr, resourceName, ExecuteCB, CompleteCB,
    26. asyncContext, &asyncContext->asyncWork);
    27. // 将异步任务加入队列
    28. napi_queue_async_work(env, asyncContext->asyncWork);
    29. return nullptr;
    30. }
  2. 定义异步任务的第一个回调函数,该函数在工作线程中执行,处理具体的业务逻辑。

    收起
    自动换行
    深色代码主题
    复制
    1. static void ExecuteCB(napi_env env, void *data)
    2. {
    3. CallbackData *callbackData = reinterpret_cast<CallbackData *>(data);
    4. callbackData->result = callbackData->args[0] + callbackData->args[1];
    5. }
  3. 定义异步任务的第二个回调函数,该函数在主线程执行,将结果传递给ArkTS侧。

    收起
    自动换行
    深色代码主题
    复制
    1. static void CompleteCB(napi_env env, napi_status status, void *data)
    2. {
    3. CallbackData *callbackData = reinterpret_cast<CallbackData *>(data);
    4. napi_value callbackArg[1] = {nullptr};
    5. napi_create_double(env, callbackData->result, &callbackArg[0]);
    6. napi_value callback = nullptr;
    7. napi_get_reference_value(env, callbackData->callbackRef, &callback);
    8. // 执行回调函数
    9. napi_value result;
    10. napi_value undefined;
    11. napi_get_undefined(env, &undefined);
    12. napi_call_function(env, undefined, callback, 1, callbackArg, &result);
    13. // 删除napi_ref对象以及异步任务
    14. napi_delete_reference(env, callbackData->callbackRef);
    15. napi_delete_async_work(env, callbackData->asyncWork);
    16. delete callbackData;
    17. callbackData = nullptr;
    18. }
  4. 模块注册以及ArkTS侧调用接口。

    导出方法名与上面一致,可直接复用模块注册的代码。

    收起
    自动换行
    深色代码主题
    复制
    1. // 模块初始化
    2. static napi_value Init(napi_env env, napi_value exports)
    3. {
    4. napi_property_descriptor desc[] = {
    5. { "asyncWork", nullptr, AsyncWork, nullptr, nullptr, nullptr, napi_default, nullptr }
    6. };
    7. napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    8. return exports;
    9. }

    接口对应的.d.ts描述。

    收起
    自动换行
    深色代码主题
    复制
    1. export const asyncWork: (arg1: number, arg2: number, callback: (result: number) => void) => void;

    ArkTS侧调用接口。

    收起
    自动换行
    深色代码主题
    复制
    1. import { hilog } from '@kit.PerformanceAnalysisKit';
    2. import nativeModule from 'libentry1.so';
    3. let num1: number = 123;
    4. let num2: number = 456;
    收起
    自动换行
    深色代码主题
    复制
    1. nativeModule.asyncWork(num1, num2, (result: number) => {
    2. hilog.info(0x0000, 'XXX', 'result is %{public}d', result);
    3. });
    收起
    自动换行
    深色代码主题
    复制
    1. 运行结果:result is 579

子线程交互场景介绍

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

基于Worker实现的C++子线程与ArkTS子线程交互场景

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

    收起
    自动换行
    深色代码主题
    复制
    1. "buildOption": {
    2. "sourceOption": {
    3. "workers": [
    4. "./src/main/ets/workers/Worker.ets"
    5. ]
    6. },
    7. }
  2. Worker线程示例代码。

    收起
    自动换行
    深色代码主题
    复制
    1. // entry/src/main/ets/workers/Worker.ets
    2. import nativeModule from 'libentry1.so';
    3. import { worker, MessageEvents } from '@kit.ArkTS';
    4. const port = worker.workerPort;
    5. port.onmessage = (e : MessageEvents) => {
    6. console.info('Worker thread received data:', e.data.num1 + '、' + e.data.num2);
    7. nativeModule.asyncWork(e.data.num1, e.data.num2, (result: number) => {
    8. port.postMessage(result);
    9. });
    10. }
  3. ArkTS线程代码。

    收起
    自动换行
    深色代码主题
    复制
    1. import { hilog } from '@kit.PerformanceAnalysisKit';
    2. import { worker } from '@kit.ArkTS';
    3. let num1: number = 123;
    4. let num2: number = 456;
    收起
    自动换行
    深色代码主题
    复制
    1. const wk = new worker.ThreadWorker('entry/ets/workers/Worker.ets');
    2. wk.postMessage({num1, num2});
    3. wk.onmessage = (msg) => {
    4. console.info('result is:', msg.data);
    5. wk.terminate();
    6. }
    收起
    自动换行
    深色代码主题
    复制
    1. 运行结果:
    2. Worker thread received data: 123、456
    3. result is 579

基于Taskpool实现的C++子线程与ArkTS子线程交互场景

  1. ArkTS线程代码。

    收起
    自动换行
    深色代码主题
    复制
    1. import { hilog } from '@kit.PerformanceAnalysisKit';
    2. import { taskpool } from '@kit.ArkTS';
    3. import nativeModule from 'libentry1.so';
    4. let num1: number = 123;
    5. let num2: number = 456;
    6. @Concurrent
    7. function nativeCall(num1 : number, num2 : number): void {
    8. console.info('Taskpool thread received data:', + num1 + '、' + num2);
    9. nativeModule.asyncWork(num1, num2, (result: number) => {
    10. hilog.info(0x0000, 'XXX', 'result is: %{public}d', result);
    11. });
    12. }
    13. async function testTaskpool() : Promise<void> {
    14. try {
    15. const task = new taskpool.Task(nativeCall, num1, num2);
    16. await taskpool.execute(task);
    17. } catch (e) {
    18. console.error(`Taskpool execute error: ${e}`);
    19. }
    20. }
    收起
    自动换行
    深色代码主题
    复制
    1. testTaskpool();
    收起
    自动换行
    深色代码主题
    复制
    1. 运行结果:
    2. Taskpool thread received data: 123、456
    3. 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系列接口,这些接口是保序的。具体使用方法可参考链接
在 指南 中进行搜索
请输入您想要搜索的关键词