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 26 27## 使用Promise方式示例 28 29 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 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)。