HarmonyOS鸿蒙Next NAPI异步调用入门
HarmonyOS鸿蒙Next NAPI异步调用入门
HarmonyOS Next NAPI异步调用入门
为什么需要异步调用?
TS是单线程,如果TS调用C++方法执行耗时任务,比如文件操作、网络请求、数据库操作、图像处理等需要在C++层创建线程来异步执行。如果需要获取异步任务返回结果一般通过回调方式获取。如果要维护原生线程和主线程之间同步需要一些工作量,NAPI基于异步 I/O 库libuv提供了异步调用机制,统一管理线程等,简化开发者使用。
NAPI异步调用介绍
HarmonyOS Next基于OpenHarmony Napi 标准系统异步接口实现支持Callback方式和Promise方式。
异步调用API介绍
- 创建异步work:
napi_create_async_work()
napi_status napi_create_async_work(
napi_env env,
napi_value async_resource,
napi_value async_resource_name,
napi_async_execute_callback execute,
napi_async_complete_callback complete,
void* data,
napi_async_work* result);
参数说明:
-
【in】env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。 -
【in】async_resource: 可选项,关联async_hooks。 -
【in】async_resource_name: 异步资源标识符,主要用于async_hooks API暴露断言诊断信息。 -
【in】execute: 执行业务逻辑计算函数,由worker线程池调度执行。在该函数中执行IO、CPU密集型任务,不阻塞主线程。 -
【in】complete: execute参数指定的函数执行完成或取消后,触发执行该函数。此函数在EventLoop线程中执行。 -
【in】data: 用户提供的上下文数据,用于传递数据。 -
【out】result:napi_async_work*指针,用于返回当前此处函数调用创建的异步工作项。返回值:返回napi_ok表示转换成功,其他值失败。 -
将异步work加入执行队列:
napi_queue_async_work()
napi_status napi_queue_async_work(
napi_env env,
napi_async_work work);
参数说明:
-
【in】env: 入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。 -
【in】work:napi_create_async_work创建的句柄 -
取消异步work:
napi_cancel_async_work
napi_status napi_cancel_async_work(
napi_env env,
napi_async_work work);
参数说明:
【in】env: 入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。【in】work:napi_create_async_work创建的句柄
如果排队的work尚未开始,则取消这个work。如果它已经开始执行,则无法取消,并将返回 napi_generic_failure。如果成功,将使用状态值 napi_cancelled 调用完成回调。即使工作已成功取消,也不应该在完成回调调用之前删除这个work。
- 删除异步work:
napi_delete_async_work
napi_status napi_delete_async_work(
napi_env env,
napi_async_work work);
参数说明:
【in】env: 入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。【in】work:napi_create_async_work创建的句柄
异步方式实现原理
同步方式中所有的代码处理都在原生方法(主线程)中完成,异步方式依赖NAPI框架提供的napi_create_async_work()函数创建异步工作项,原生方法被调用时,原生方法完成数据接收、转换,存入上下文数据,之后创建一个异步工作项,并加入调度队列,由异步工作线程池统一调度,原生方法返回空值(Callback方式)或返回Promise对象(Promise方式)。
异步工作项中定义了2个函数,一个用于执行工作项的业务逻辑,异步工作项被调度后,该函数从上下文数据中获取输入数据,在worker线程中完成业务逻辑计算(不阻塞主线程)并将结果写入上下文数据。业务逻辑处理函数执行完成或被取消后,触发EventLoop执行另一函数,函数从上下文数据中获取结果,转换为JS类型,调用JS回调函数或通过Promise resolve()返回结果。
相较原生线程优势
- 线程管理:NodeJs提供了一个抽象层,不需要手动管理线程的生命周期或同步问题。
napi_create_async_work会在后台执行任务,并在任务完成后自动回调到 JavaScript 线程。Node.js 本身使用了 libuv 作为事件驱动机制,napi_create_async_work会将任务放入 libuv 的线程池中执行,并在任务完成后通过事件循环调用回调。 - 内存管理:NodeJs 的垃圾回收机制与异步工作结合良好。
napi_create_async_work确保了在任务完成之前,任务相关的资源不会被提前回收。任务完成后,N-API 会自动管理与 JavaScript 对象的资源同步,提供了便捷的内存和生命周期管理。 - 回调机制:
napi_create_async_work为 JavaScript 提供了一种与 C++ 异步工作交互的回调机制。任务执行完毕后,回调函数会在主线程中被执行,可以直接将任务结果传回 JavaScript 端。这种机制使得异步工作能够无缝地与 JavaScript 主线程事件循环集成。 - 线程池与并发:napi_create_async_work利用了 Node.js 的 libuv 线程池,无需开发者手动创建和管理线程。Node.js 的线程池会智能调度工作任务,我们不必担心线程池的大小或并发量。
- 错误处理:N-API 提供了较为简单的错误处理方式,任务执行期间的错误可以在回调函数中捕获并传递回 JavaScript 层。开发者可以轻松将本地(Native)代码中的错误状态传递给 JavaScript 层进行处理。
异步接口实现
以音频编码为例展示异步接口。
Callback 异步接口方式
示例接口的eTS定义
export const encodePcmDataForOpusWithOgg:(intputBuffer: ArrayBuffer, callback:() => void) => void;
传入PCM数据,通过编码完成直接写入文件,并同步TS层执行状态(也可以返回编码后数据)。
初始化上下文数据
根据业务需求自定义一个上下文数据结构,用于保存和传递数据。本例自定义的上下文数据包含:异步工作项对象、回调函数、2个参数等4个属性。
struct EncodeAudioData {
napi_async_work asyncWork = nullptr;
napi_ref callback = nullptr;
void* inputBuffer = nullptr;
size_t intputSize = 0;
}
创建异步工作项
在创建异步工作项前,我们先分别声明2个函数,分别用作于napi_create_async_work()函数的execute、complete参数。异步工作项创建好,将它存入上下文数据的asyncWork属性,并调用napi_queue_async_work()将异步工作项加入调度队列,由异步work线程池统一调度,原生方法返回空值退出。
static napi_value encodePCMToOpusOggNative(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
auto audioData = new EncodeAudioData{
.asyncWork = nullptr,
};
napi_get_arraybuffer_info(env, args[0], &audioData->inputBuffer, &(audioData->intputSize));
napi_create_reference(env, args[1], 1, &audioData->callback);
napi_value resourceName = nullptr;
napi_create_string_utf8(env, "encodePCMToOpusOggNative", NAPI_AUTO_LENGTH, &resourceName);
napi_create_async_work(env, nullptr, resourceName, encodeExecuteCB, encodeAsyncCompleteCB, (void *)audioData, &audioData->asyncWork);
napi_queue_async_work(env, audioData->asyncWork);
return nullptr;
}
execute 函数
// 业务逻辑处理函数,由worker线程池调度执行。
static void aencodeExecuteCB(napi_env env, void *data) {
EncodeAudioData *encodeAudioData = (EncodeAudioData *)data;
// 执行音频编码和写入文件
ope_encoder_write(pEnc, (short *)(encodeAudioData->inputBuffer), encodeAudioData->intputSize/2);
}
complete 函数
// 业务逻辑处理完成回调函数,在业务逻辑处理函数执行完成或取消后触发,由EventLoop线程中执行。
static void encodeAsyncCompleteCB(napi_env env, napi_status status, void *data) {
EncodeAudioData *encodeAudioData = (EncodeAudioData *)data;
napi_value callback = nullptr;
napi_value undefined = nullptr;
napi_get_undefined(env, &undefined);
napi_get_reference_value(env, encodeAudioData->callback, &callback);
napi_value callbackResult;
// 回调TS
napi_call_function(env, undefined, callback, 0, nullptr, &callbackResult);
// 释放资源
if (encodeAudioData->callback != nullptr) {
napi_delete_reference(env, encodeAudioData->callback);
}
napi_delete_async_work(env, encodeAudioData->asyncWork);
delete encodeAudioData;
}
eTS调用接口
static encodePcmDataForOpusOgg(inputBuffer: ArrayBuffer):void {
opusOggEnc.encodePcmDataForOpusWithOgg(inputBuffer, ()=>{
});
}
Promise 接口方式
通过前面异步方式实现原理我们可知Promise整体处理流程和Callback方式一样。不同的是,首先要创建一个Promise。NAPI框架中提供了napi_create_promise()函数用于创建Promise,调用该函数输出2个对象——deferred、promise。promise用于原生方法返回,deferred传入异步工作项的上下文数据。complete函数中,应用napi_resolve_deferred()函数 或 napi_reject_deferred() 函数返回数据。
创建Promise
static napi_value encodePromise(napi_env env, napi_callback_info info) {
// 创建promise
napi_value promise = nullptr;
napi_deferred deferred = nullptr;
napi_create_promise(env, &deferred, &promise);
...
// 返回promise
return promise;
}
初始化上下文数据
同Callback方式定义一个上下文数据结构,用于保存和传递数据。Promise方式去掉callback属性,加上deferred属性。
struct EncodeAudioData {
napi_async_work asyncWork = nullptr;
napi_deferred deferred = nullptr;
void* inputBuffer = nullptr;
size_t intputSize = 0;
}
创建异步工作项
static napi_value encodePromise(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// 创建promise
napi_value promise = nullptr;
napi_deferred deferred = nullptr;
napi_create_promise(env, &deferred, &promise);
// 异步工作项上下文用户数据,传递到异步工作项的execute、complete中传递数据
auto audioData = new EncodeAudioData{
.asyncWork = nullptr,
.deferred = deferred,
};
...
}
execute 回调处理
// 业务逻辑处理函数,由worker线程池调度执行。
static void encodeExecuteCB(napi_env env, void *data) {
EncodeAudioData *encodeAudioData = (EncodeAudioData *)data;
// 执行音频编码和写入文件
ope_encoder_write(pEnc, (short *)(encodeAudioData->inputBuffer), encodeAudioData->intputSize/2);
}
complete 回调处理
static void encodePromiseCompleteCB(napi_env env, napi_status status, void *data) {
EncodeAudioData *encodeAudioData = (EncodeAudioData *)data;
napi_value undefined;
napi_get_undefined(env, &undefined);
napi_resolve_deferred(env, encodeAudioData->deferred, undefined);
// 删除napi_ref对象
if (encodeAudioData->callback != nullptr) {
napi_delete_reference(env, encodeAudioData->callback);
}
// 删除异步工作项
napi_delete_async_work(env, encodeAudioData->asyncWork);
delete encodeAudioData;
encodeAudioData = nullptr;
}
eTS调用接口
static encodePcmDataForOpusOgg(inputBuffer: ArrayBuffer):void {
opusOggEnc.encodePcmDataForOpusWithOggPromise(inputBuffer).then(()=>{
});
}
规范异步接口
若引擎开启Promise特性支持,则异步方法必须同时支持Callback方式和Promise方式,通过判断接收到的参数个数判断是Callback方式还是Promise方式。
export function encodePcmDataForOpusWithOgg(intputBuffer: ArrayBuffer, callback:() => void): void;
export function encodePcmDataForOpusWithOgg(intputBuffer: ArrayBuffer): Promise<void>;
首先修改用户上下文数据结构,同时包含deferred、callback属性。
struct EncodeAudioData {
napi_async_work asyncWork = nullptr;
napi_ref callback = nullptr;
napi_deferred deferred = nullptr;
void* inputBuffer = nullptr;
size_t intputSize = 0;
}
修改接口原生方法实现,通过判断实际获取到的参数个数判断是Callback还是Promise,根据上面的接口定义,2个参数是Promise,3个参数是Callback。
static napi_value encodePCMToOpusOggNative(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
auto audioData = new EncodeAudioData{
.asyncWork = nullptr,
};
if (argc == 1) {
// 创建promise
napi_value promise = nullptr;
napi_deferred deferred = nullptr;
napi_create_promise(env, &deferred, &promise);
audioData->deferred = deferred;
napi_get_arraybuffer_info(env, args[0], &audioData->inputBuffer, &(audioData->intputSize));
napi_create_reference(env, args[0], 1, &audioData->callback);
napi_value resourceName = nullptr;
napi_create_string_utf8(env, "encodePCMToOpusOggNativePromise", NAPI_AUTO_LENGTH, &resourceName);
napi_create_async_work(env, nullptr, resourceName, encodeExecuteCB, encodePromiseCompleteCB, (void *)audioData, &audioData->asyncWork);
napi_queue_async_work(env, audioData->asyncWork);
// 返回promise
return promise;
}else{
napi_get_arraybuffer_info(env, args[0], &audioData->inputBuffer, &(audioData->intputSize));
napi_create_reference(env, args[1], 1, &audioData->callback);
napi_value resourceName = nullptr;
napi_create_string_utf8(env, "encodePCMToOpusOggNativeCallback", NAPI_AUTO_LENGTH, &resourceName);
napi_create_async_work(env, nullptr, resourceName, encodeExecuteCB, encodeAsyncCompleteCB, (void *)audioData, &audioData->asyncWork);
napi_queue_async_work(env, audioData->asyncWork);
return nullptr;
}
}
更多关于HarmonyOS鸿蒙Next NAPI异步调用入门的实战教程也可以访问 https://www.itying.com/category-93-b0.html
在鸿蒙Next中,异步调用的核心是使用napi_create_async_work、napi_queue_async_work和napi_delete_async_work等函数。这些函数允许在Native层创建、执行和销毁异步任务。具体步骤如下:
-
创建异步任务:使用
napi_create_async_work创建一个异步工作对象。该函数需要提供执行函数(execute)和完成回调函数(complete)。execute函数在异步线程中执行耗时操作,complete函数在主线程中执行,用于处理异步操作的结果。 -
提交异步任务:通过
napi_queue_async_work将异步任务提交到任务队列中,等待执行。任务会在后台线程中运行,不会阻塞主线程。 -
处理异步结果:在
complete回调函数中,可以将异步操作的结果传递回ArkTS层。通常使用napi_get_value_*系列函数从Native层获取数据,并通过napi_call_function调用ArkTS中的回调函数。 -
清理异步任务:异步任务执行完毕后,使用
napi_delete_async_work释放资源。
以下是一个简单的示例,展示了如何在Native层实现异步调用:
#include <napi.h>
void ExecuteWork(napi_env env, void* data) {
// 模拟耗时操作
sleep(2);
}
void CompleteWork(napi_env env, napi_status status, void* data) {
napi_value undefined;
napi_get_undefined(env, &undefined);
napi_value callback = (napi_value)data;
napi_call_function(env, undefined, callback, 0, nullptr, nullptr);
}
void AsyncCall(napi_env env, napi_callback_info info) {
napi_value work_name;
napi_create_string_utf8(env, "AsyncWork", NAPI_AUTO_LENGTH, &work_name);
napi_value callback;
napi_get_cb_info(env, info, nullptr, nullptr, &callback, nullptr);
napi_async_work work;
napi_create_async_work(env, nullptr, work_name, ExecuteWork, CompleteWork, callback, &work);
napi_queue_async_work(env, work);
}
NAPI_MODULE_INIT() {
napi_value fn;
napi_create_function(env, nullptr, 0, AsyncCall, nullptr, &fn);
napi_set_named_property(env, exports, "asyncCall", fn);
return exports;
}
在ArkTS中,可以通过以下方式调用该异步函数:
import nativeModule from 'libnative.so';
nativeModule.asyncCall(() => {
console.log('Async operation completed.');
});
通过NAPI实现异步调用,可以有效提升应用的响应速度和性能。
更多关于HarmonyOS鸿蒙Next NAPI异步调用入门的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
HarmonyOS鸿蒙Next的NAPI(Native API)允许开发者进行异步调用,以提升应用性能。首先,通过napi_create_async_work创建异步工作对象,定义异步任务。然后,使用napi_queue_async_work将任务加入队列,系统会在后台执行。任务完成后,通过napi_delete_async_work释放资源。异步调用的关键在于正确处理回调函数,确保数据同步和错误处理。开发者需熟悉Node.js的异步编程模型,以便在鸿蒙平台上高效实现异步操作。

