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