• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 使用Node-API接口进行异步任务开发
2<!--Kit: NDK-->
3<!--Subsystem: arkcompiler-->
4<!--Owner: @xliu-huanwei; @shilei123; @huanghello-->
5<!--Designer: @shilei123-->
6<!--Tester: @kirl75; @zsw_zhushiwei-->
7<!--Adviser: @fang-jinxu-->
8
9## 场景介绍
10
11[napi_create_async_work](../reference/native-lib/napi.md#napi_create_async_work)是Node-API接口之一,用于创建一个异步工作对象。在需要执行耗时操作的场景中使用,避免阻塞env所在的ArkTS线程,确保应用程序的性能和响应性能。例如以下场景:
12
13- 文件操作:读取大型文件或执行复杂的文件操作时,可以使用异步工作对象来避免阻塞env所在的ArkTS线程。
14
15- 网络请求:当需要进行网络请求并等待响应时,使用异步工作对象确保主线程不被阻塞,提高应用程序的响应性能。
16
17- 数据库操作:当需要执行复杂的数据库查询或写入操作时,使用异步工作对象确保主线程不被阻塞,提高应用程序的并发性能。
18
19- 图像处理:当需要对大型图像进行处理或执行复杂的图像算法时,使用异步工作对象确保主线程不被阻塞,提高应用程序的实时性能。
20
21napi_queue_async_work接口使用uv_queue_work能力,并管理回调中napi_value的生命周期。
22
23异步调用支持callback和Promise两种方式,选择哪种方式由开发者决定。以下是两种方式的示例代码:
24
25![NAPI 异步任务线程](figures/napi_async_work.png)
26
27## 使用Promise方式示例
28
29![NAPI Promise异步流程](figures/napi_async_work_with_promise.png)
30
311. 使用napi_create_async_work创建异步任务,使用napi_queue_async_work将任务加入队列,等待执行。
32
33   ```cpp
34   // 调用方提供的data context,该数据会传递给execute和complete函数
35   struct CallbackData {
36       napi_async_work asyncWork = nullptr;
37       napi_deferred deferred = nullptr;
38       napi_ref callback = nullptr;
39       double args = 0;
40       double result = 0;
41   };
42
43   static napi_value AsyncWork(napi_env env, napi_callback_info info)
44   {
45      size_t argc = 1;
46      napi_value args[1];
47      napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
48
49      napi_value promise = nullptr;
50      napi_deferred deferred = nullptr;
51      napi_create_promise(env, &deferred, &promise);
52
53      auto callbackData = new CallbackData();
54      callbackData->deferred = deferred;
55      napi_get_value_double(env, args[0], &callbackData->args);
56
57      napi_value resourceName = nullptr;
58      napi_create_string_utf8(env, "AsyncCallback", NAPI_AUTO_LENGTH, &resourceName);
59      // 创建异步任务
60      napi_create_async_work(env, nullptr, resourceName, ExecuteCB, CompleteCB, callbackData, &callbackData->asyncWork);
61      // 将异步任务加入队列
62      napi_queue_async_work(env, callbackData->asyncWork);
63
64      return promise;
65   }
66   ```
67   <!-- @[napi_create_async_work](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/NodeAPI/NodeAPIClassicUseCases/NodeAPIAsynchronousTask/entry/src/main/cpp/napi_init.cpp) -->
68
692. 定义异步任务的第一个回调函数,该函数在工作线程中执行,处理具体的业务逻辑。
70
71   ```cpp
72   static void ExecuteCB(napi_env env, void *data)
73   {
74       CallbackData *callbackData = reinterpret_cast<CallbackData *>(data);
75       callbackData->result = callbackData->args;
76   }
77   ```
78   <!-- @[napi_first_call_back_work](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/NodeAPI/NodeAPIClassicUseCases/NodeAPIAsynchronousTask/entry/src/main/cpp/napi_init.cpp) -->
79
803. 定义异步任务的第二个回调函数,该函数在主线程执行,将结果传递给ArkTS侧。
81
82   ```cpp
83   static void CompleteCB(napi_env env, napi_status status, void *data)
84   {
85       CallbackData *callbackData = reinterpret_cast<CallbackData *>(data);
86       napi_value result = nullptr;
87       napi_create_double(env, callbackData->result, &result);
88       if (callbackData->result > 0) {
89           napi_resolve_deferred(env, callbackData->deferred, result);
90       } else {
91           napi_reject_deferred(env, callbackData->deferred, result);
92       }
93
94       napi_delete_async_work(env, callbackData->asyncWork);
95       delete callbackData;
96       callbackData = nullptr;
97   }
98   ```
99   <!-- @[napi_second_call_back_main](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/NodeAPI/NodeAPIClassicUseCases/NodeAPIAsynchronousTask/entry/src/main/cpp/napi_init.cpp) -->
100
1014. 模块初始化和ArkTS侧调用接口。
102
103   ```cpp
104   // 模块初始化
105   static napi_value Init(napi_env env, napi_value exports)
106   {
107       napi_property_descriptor desc[] = {
108           { "asyncWork", nullptr, AsyncWork, nullptr, nullptr, nullptr, napi_default, nullptr }
109       };
110       napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
111       return exports;
112   }
113    ```
114   <!-- @[napi_value_init](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/NodeAPI/NodeAPIClassicUseCases/NodeAPIAsynchronousTask/entry/src/main/cpp/callback.cpp) -->
115
116    ```ts
117   // 接口对应的.d.ts描述
118   export const asyncWork: (data: number) => Promise<number>;
119
120   // ArkTS侧调用接口
121   nativeModule.asyncWork(1024).then((result) => {
122       hilog.info(0x0000, 'XXX', 'result is %{public}d', result);
123   });
124   ```
125   运行结果:result is 1024
126
127## 使用callback方式示例
128
129![NAPI Callback异步流程](figures/napi_async_work_with_callback.png)
130
1311. 使用napi_create_async_work创建异步任务,并使用napi_queue_async_work将异步任务加入队列,等待执行。
132
133   ```cpp
134   static constexpr int INT_ARG_2 = 2; // 入参索引
135
136   // 调用方提供的data context,该数据会传递给execute和complete函数
137   struct CallbackData {
138       napi_async_work asyncWork = nullptr;
139       napi_ref callbackRef = nullptr;
140       double args[2] = {0};
141       double result = 0;
142   };
143
144   napi_value AsyncWork(napi_env env, napi_callback_info info)
145   {
146       size_t argc = 3;
147       napi_value args[3];
148       napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
149       auto asyncContext = new CallbackData();
150       // 将接收到的参数保存到callbackData
151       napi_get_value_double(env, args[0], &asyncContext->args[0]);
152       napi_get_value_double(env, args[1], &asyncContext->args[1]);
153       // 将传入的callback转换为napi_ref延长其生命周期,防止被GC掉
154       napi_create_reference(env, args[INT_ARG_2], 1, &asyncContext->callbackRef);
155       napi_value resourceName = nullptr;
156       napi_create_string_utf8(env, "asyncWorkCallback", NAPI_AUTO_LENGTH, &resourceName);
157       // 创建异步任务
158       napi_create_async_work(env, nullptr, resourceName, ExecuteCB, CompleteCB,
159                              asyncContext, &asyncContext->asyncWork);
160       // 将异步任务加入队列
161       napi_queue_async_work(env, asyncContext->asyncWork);
162       return nullptr;
163   }
164   ```
165   <!-- @[napi_create_queue_async_work](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/NodeAPI/NodeAPIClassicUseCases/NodeAPIAsynchronousTask/entry/src/main/cpp/callback.cpp) -->
166
1672. 定义异步任务的第一个回调函数,该函数在工作线程中执行,处理具体的业务逻辑。
168
169   ```cpp
170   static void ExecuteCB(napi_env env, void *data)
171   {
172       CallbackData *callbackData = reinterpret_cast<CallbackData *>(data);
173       callbackData->result = callbackData->args[0] + callbackData->args[1];
174   }
175   ```
176   <!-- @[napi_async_first_call_back_work](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/NodeAPI/NodeAPIClassicUseCases/NodeAPIAsynchronousTask/entry/src/main/cpp/callback.cpp) -->
177
1783. 定义异步任务的第二个回调函数,该函数在主线程执行,将结果传递给ArkTS侧。
179
180   ```cpp
181   static void CompleteCB(napi_env env, napi_status status, void *data)
182   {
183       CallbackData *callbackData = reinterpret_cast<CallbackData *>(data);
184       napi_value callbackArg[1] = {nullptr};
185       napi_create_double(env, callbackData->result, &callbackArg[0]);
186       napi_value callback = nullptr;
187       napi_get_reference_value(env, callbackData->callbackRef, &callback);
188       // 执行回调函数
189       napi_value result;
190       napi_value undefined;
191       napi_get_undefined(env, &undefined);
192       napi_call_function(env, undefined, callback, 1, callbackArg, &result);
193       // 删除napi_ref对象以及异步任务
194       napi_delete_reference(env, callbackData->callbackRef);
195       napi_delete_async_work(env, callbackData->asyncWork);
196       delete callbackData;
197       callbackData = nullptr;
198   }
199   ```
200   <!-- @[napi_async_second_call_back_work](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/NodeAPI/NodeAPIClassicUseCases/NodeAPIAsynchronousTask/entry/src/main/cpp/callback.cpp) -->
201
2024. 模块初始化以及ArkTS侧调用接口。
203
204   ```cpp
205   // 模块初始化
206   static napi_value Init(napi_env env, napi_value exports)
207   {
208       napi_property_descriptor desc[] = {
209           { "asyncWork", nullptr, AsyncWork, nullptr, nullptr, nullptr, napi_default, nullptr }
210       };
211       napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
212       return exports;
213   }
214   ```
215   <!-- @[napi_value_init](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/NodeAPI/NodeAPIClassicUseCases/NodeAPIAsynchronousTask/entry/src/main/cpp/callback.cpp) -->
216
217   ```ts
218   // 接口对应的.d.ts描述
219   export const asyncWork: (arg1: number, arg2: number, callback: (result: number) => void) => void;
220
221   // ArkTS侧调用接口
222   let num1: number = 123;
223   let num2: number = 456;
224   nativeModule.asyncWork(num1, num2, (result) => {
225       hilog.info(0x0000, 'XXX', 'result is %{public}d', result);
226   });
227   ```
228   运行结果:result is 579
229
230## 注意事项
231- 调用napi_cancel_async_work接口,无论底层uv是否失败都会返回napi_ok。若因为底层uv导致取消任务失败,complete callback中的status会传入对应错误值,请在complete callback中对status进行处理。
232- NAPI的异步工作项(napi_async_work)建议单次使用。napi_queue_async_work后,该napi_async_work需在complete回调执行时或执行后,通过napi_delete_async_work完成释放。同一个napi_async_work只允许释放一次,重复释放会导致未定义行为。
233`napi_async_work`的`execute_cb`运行在一个独立的工作线程中,该线程从uv线程池中取出。不同工作线程之间互不影响。
234- 在任务的执行时序上,`napi_async_work`仅保证`complete_cb`在`execute_cb`之后执行。不同`napi_async_work`的`execute_cb`在各自的工作线程上运行,因此无法保证不同`execute_cb`的执行顺序。如果任务执行需要顺序,建议使用`napi_threadsafe_function`系列接口,这些接口是保序的。具体使用方法可参考[链接](use-napi-thread-safety.md)。