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 20 21## 使用Promise方式示例 22 23 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 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