• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Asynchronous Task Development Using Node-API
2
3## When to Use
4
5For time-consuming operations, you can use [napi_create_async_work](../reference/native-lib/napi.md#napi_create_async_work) to create an asynchronous work object to prevent the ArkTS thread where env exists from being blocked while ensuring the performance and response of your application. You can use asynchronous work objects in the following scenarios:
6
7- File operations: You can use asynchronous work objects in complex file operations or when a large file needs to be read to prevent the ArkTS thread where env exists from being blocked.
8
9- Network request: When your application needs to wait for a response to a network request, using an asynchronous worker object can improve its response performance without affecting the main thread.
10
11- Database operation: Using asynchronous work objects in complex database query or write operations can improve the concurrency performance of your application without compromising the running of the main thread.
12
13- Image processing: When large images need to be processed or complex image algorithms need to be executed, asynchronous work objects can ensure normal running of the main thread and improve the real-time performance of your application.
14
15The **napi_queue_async_work** API uses the **uv_queue_work** capability at the bottom layer and manages the lifecycle of **napi_value** in the callback.
16
17You can use a promise or a callback to implement asynchronous calls. The following are sample codes for the two methods:
18
19![](figures/napi_async_work.png)
20
21## Example (Promise)
22
23![](figures/napi_async_work_with_promise.png)
24
251. Call **napi_create_async_work** to create an asynchronous work object, and call **napi_queue_async_work** to add the object to a queue.
26
27   ```cpp
28   // Data context provided by the caller. The data is transferred to the execute and complete functions.
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      // Create an asynchronous work object.
54      napi_create_async_work(env, nullptr, resourceName, ExecuteCB, CompleteCB, callbackData, &callbackData->asyncWork);
55      // Add the asynchronous work object to a queue.
56      napi_queue_async_work(env, callbackData->asyncWork);
57
58      return promise;
59   }
60   ```
61
622. Define the first callback of the asynchronous work object. This callback is executed in a worker thread to process specific service logic.
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. Define the second callback of the asynchronous work object. This callback is executed in the main thread to return the result to the ArkTS side.
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. Initialize the module and call the API of ArkTS.
92
93   ```cpp
94   // Initialize the module.
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   // Description of the interface in the .d.ts file.
107   export const asyncWork: (data: number) => Promise<number>;
108
109   // Call the API of ArkTS.
110   nativeModule.asyncWork(1024).then((result) => {
111       hilog.info(0x0000, 'XXX', 'result is %{public}d', result);
112     });
113   ```
114   Result: **result is 1024**
115
116## Example (Callback)
117
118![](figures/napi_async_work_with_callback.png)
119
1201. Call **napi_create_async_work** to create an asynchronous work object, and call **napi_queue_async_work** to add the object to a queue.
121
122   ```cpp
123   static constexpr int INT_ARG_2 = 2; // Input parameter index.
124
125   // Data context provided by the caller. The data is transferred to the execute and complete functions.
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       // Save the received parameters to callbackData.
140       napi_get_value_double(env, args[0], &asyncContext->args[0]);
141       napi_get_value_double(env, args[1], &asyncContext->args[1]);
142       // Convert the callback to napi_ref to extend its lifecycle to prevent it from being garbage-collected.
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       // Create an asynchronous work object.
147       napi_create_async_work(env, nullptr, resourceName, ExecuteCB, CompleteCB,
148                              asyncContext, &asyncContext->asyncWork);
149       // Add the asynchronous work object to a queue.
150       napi_queue_async_work(env, asyncContext->asyncWork);
151       return nullptr;
152   }
153   ```
154
1552. Define the first callback of the asynchronous work object. This callback is executed in a worker thread to process specific service logic.
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. Define the second callback of the asynchronous work object. This callback is executed in the main thread to return the result to the ArkTS side.
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       // Execute the callback.
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       // Delete the napi_ref object and asynchronous work object.
181       napi_delete_reference(env, callbackData->callbackRef);
182       napi_delete_async_work(env, callbackData->asyncWork);
183       delete callbackData;
184   }
185   ```
186
1874. Initialize the module and call the API of ArkTS.
188
189   ```cpp
190   // Initialize the module.
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   // Description of the interface in the .d.ts file.
203   export const asyncWork: (arg1: number, arg2: number, callback: (result: number) => void) => void;
204
205   // Call the API of 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## NOTE
214- When the **napi_cancel_async_work** API is called, **napi_ok** is returned regardless of whether the underlying UV fails. If the task fails to be canceled due to the underlying UV, the corresponding error value is transferred to **status** in the complete callback. You need to perform the corresponding operation based on the value of **status**.
215- It is recommended that the asynchronous work item of Node-API (**napi_async_work**) be used only once. After **napi_queue_async_work** is called, you should release it through **napi_delete_async_work** during or after the execution of the **complete** callback. The same **napi_async_work** can be released only once. Repeated release attempts will cause undefined behavior.
216