使用Node-API进行异步任务开发

2024-09-08 06:20

本文主要是介绍使用Node-API进行异步任务开发,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、Node-API异步任务机制概述

        Node-API异步任务开发主要用于执行耗时操作的场景中使用,以避免阻塞主线程,确保应用程序的性能和响应效率。

        1、应用场景:

  • 文件操作:读取大型文件或执行复杂的文件操作时,可以使用异步工作项来避免阻塞主线程。
  • 网络请求:当需要进行网络请求并等待响应时,可以使用异步工作项来避免阻塞主线程,从而提高应用程序的响应性能。
  • 数据库操作:当需要执行复杂的数据库查询或写入操作时,可以使用异步工作项来避免阻塞主线程,从而提高应用程序的并发性能。
  • 图形处理:当需要对大型图像进行处理或执行复杂的图像算法时,可以使用异步工作项来避免阻塞主线程,从而提高应用程序的实时性能。

        2、 异步方式与同步方式的区别

        异步方式与同步方式的区别在于,同步方式中所有代码的处理都在ArkTS主线程中完成,而异步方式中的所有代码在多线程中完成。Node-API主要是通过创建一个异步工作项来实现异步任务开发。

        3、异步任务开发总体步骤

  • 在Native接口函数中,创建一个异步工作项,并置入libuv调度队列中,然后立即返回一个临时结果给ArkTS调用者;
  • 通过libuv线程池创建并调度work子线程完成异步业务逻辑的执行;
  • 通过Callback回调或者Promise延时对象返回真正的处理结果,并用于应用侧UI刷新。

        4、异步任务流程原理 

        异步工作项的底层机制是基于libuv异步库来实现的,具体流程原理如下:

        

        依赖Node-API提供的napi_create_async_work接口创建异步工作项:

NAPI_EXTERN 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:传入接口调用者的环境,包含方舟引擎等。由框架提供,默认情况下直接传入即可。
[in] async_resource:可选项,关联async_hooks。
[in] async_resource_name:异步资源标识符,主要用于async_hooks API暴露断言诊断信息。
[in] execute:执行业务逻辑计算函数,由libuv线程池调度执行。在该函数中执行IO、CPU密集型任务,不阻塞主线程。
[in] complete:execute回调函数执行完成或取消后,触发执行该函数。此函数在EventLoop子线程中执行。
[in] data:用户提供的上下文数据,用于传递数据。
[out] result:napi_async_work*指针,用于返回当前此处函数调用创建的异步工作项。 返回值:返回napi_ok表示转换成功,其他值失败。

        5、Execute回调

  • execute函数用于执行工作项的业务逻辑,异步工作项被调度后,该函数从上下文数据中获取输入数据,在work子线程中完成业务逻辑计算(不阻塞主线程)并将结果写入上下文数据。
  • 因为execute函数不在ArkTS线程中,所以不允许execute函数调用napi的接口。业务逻辑的返回值可以返回到complete回调中处理。

        6、Complete回调

  • 业务逻辑处理execute函数执行完成或被取消后,通过事件通知EventLoop执行complete函数,complete函数从上下文数据中获取结果,转换为napi_value类型,调用ArkTS回调函数或通过Promise resolve()返回结果。
  • 该函数运行在ArkTS主线程下,因此可以调用napi的接口,将execute中的返回值封装成ArkTS对象返回。

        7、两种异步模型 

        Node-API异步接口实现支持Callback方式和Promise方式,具体使用哪种方式由应用开发者决定,通过是否传递callback函数进行区分。

        (1)Callback异步模型

  • 用户在调用Native接口的时候,Native接口将异步执行任务,并临时返回空值给ArkTS应用侧。
  • 异步任务执行结果以参数的形式提供给用户注册的ArkTS回调函数,并通过napi_call_function将ArkTS回调函数进行调用执行以反馈结果到ArkTS应用侧。

        (2)Promise异步模型

  • 用户在调用Native接口的时候,Native接口将异步执行任务,并返回一个Promise对象给ArkTS应用侧。
  • Promise对象提供了API使得异步执行可以按照同步的流程表示出来,避免了层层嵌套的回调引用。
  • 异步任务执行结果以参数的形式提供给与ArkTS应用侧Promise对象关联的deferred对象,并通过napi_resolve_deferred将计算结果反馈到ArkTS应用侧。

二、异步任务开发时序交互图 

        1、Callback异步开发时序交互

        

        2、Promise异步开发时序交互 

        

三、异步任务开发步骤(Callback简介) 

        

        

        

        

        

四、异步任务开发步骤(实例) 

        1、Callback异步开发步骤

        (1)ArkTS应用侧开发

//Index.ets文件import testNapi from 'libentry.so';
import Constants from '../../common/constants/CommonConstants';@Entry
@Component
struct Index {@State imagePath: string = Constants.INIT_IMAGE_PATH;imageName: string = '';build() {Column() {...// button list, prompting the user to click the button to select the target image.Column() {...// multi-threads callback async buttonButton($r('app.string.async_callback_button_title')).width(Constants.FULL_PARENT).margin($r('app.float.button_common_margin')).onClick(() => {this.imageName = Constants.CALLBACK_BUTTON_IMAGE;testNapi.getImagePathAsyncCallBack(this.imageName, (result: string) => {this.imagePath = Constants.IMAGE_ROOT_PATH + result;});})...}...}...}
}

        (2)Native侧开发

        导出Native接口:将Native接口导出到ArkTS侧,用于支撑ArkTS对象调用和模块编译构建。

// index.d.ts文件
export const getImagePathAsyncCallBack: (imageName: string, callBack: (result: string) => void) => void;

        execute回调:定义异步工作项的第一个回调函数,该函数在work子线程中执行,处理具体的业务逻辑。

// MultiThreads.cpp文件static void ExecuteFunc([[maybe_unused]] napi_env env, void *data) {// create producer threadthread producer(ProductElement, data);// the producer and consumer threads must be synchronized// otherwise, the complete operation is triggered to communicate with the ArkTS after the executeFunc is complete// the result is unpredictableproducer.join();// create consumer threadthread consumer(ConsumeElement, data);consumer.join();
}

        complete回调:定义异步工作项的第二个回调函数,该函数在ArkTS主线程中执行,将结果传递给ArkTS侧。

// MultiThreads.cpp文件static void CompleteFuncCallBack(napi_env env, [[maybe_unused]] napi_status status, void *data) {// parse context dataContextData *contextData = static_cast<ContextData *>(data);napi_value callBack = nullptr;napi_status operStatus = napi_get_reference_value(env, contextData->callbackRef, &callBack);if (operStatus != napi_ok) {DeleteContext(env, contextData);return;}// define the undefined variable, which is used in napi_call_function// because no other data is transferred, the variable is defined as undefinednapi_value undefined = nullptr;operStatus = napi_get_undefined(env, &undefined);if (operStatus != napi_ok) {DeleteContext(env, contextData);return;}// convert the calculation result of C++ sub-thread to the napi_value typenapi_value callBackArgs = nullptr;operStatus = napi_create_string_utf8(env, contextData->result.c_str(),contextData->result.length(), &callBackArgs);if (operStatus != napi_ok) {DeleteContext(env, contextData);return;}// call the JS callback and send the async calculation result on the Native to ArkTS applicationnapi_value callBackResult = nullptr;(void)napi_call_function(env, undefined, callBack, 1, &callBackArgs, &callBackResult);// destroy data and release memoryDeleteContext(env, contextData);
}

        Native异步任务开发接口:解析ArkTS应用侧参数,使用napi_create_async_work创建异步工作项,并使用napi_queue_async_work将异步任务加入队列,等待调度执行。

// MultiThreads.cpp文件// callback async interface
static napi_value GetImagePathAsyncCallBack(napi_env env, napi_callback_info info) {size_t paraNum = 2;napi_value paraArray[2] = {nullptr};// parse parametersnapi_status operStatus = napi_get_cb_info(env, info, &paraNum, paraArray, nullptr, nullptr);if (operStatus != napi_ok) {return nullptr;}napi_valuetype paraDataType = napi_undefined;operStatus = napi_typeof(env, paraArray[0], &paraDataType);if ((operStatus != napi_ok) || (paraDataType != napi_string)) {return nullptr;}operStatus = napi_typeof(env, paraArray[1], &paraDataType);if ((operStatus != napi_ok) || (paraDataType != napi_function)) {return nullptr;}// napi_value convert to char *constexpr size_t buffSize = 100;char strBuff[buffSize]{}; // char buffer for imageName stringsize_t strLength = 0;operStatus = napi_get_value_string_utf8(env, paraArray[0], strBuff, buffSize, &strLength);if ((operStatus != napi_ok) || (strLength == 0)) {return nullptr;}// defines context data. the memory will be released in CompleteFuncauto contextData = new ContextData;contextData->args = strBuff;operStatus = napi_create_reference(env, paraArray[1], 1, &contextData->callbackRef);if (operStatus != napi_ok) {DeleteContext(env, contextData);return nullptr;}// async resourcenapi_value asyncName = nullptr;string asyncStr = "async callback";operStatus = napi_create_string_utf8(env, asyncStr.c_str(), asyncStr.length(), &asyncName);if (operStatus != napi_ok) {DeleteContext(env, contextData);return nullptr;}// create async workoperStatus = napi_create_async_work(env, nullptr, asyncName, ExecuteFunc, CompleteFuncCallBack,static_cast<void *>(contextData), &contextData->asyncWork);if (operStatus != napi_ok) {DeleteContext(env, contextData);return nullptr;}// add the async work to the queue and wait for schedulingoperStatus = napi_queue_async_work(env, contextData->asyncWork);if (operStatus != napi_ok) {DeleteContext(env, contextData);}return nullptr;
}

        2、Promise异步开发步骤

        (1)ArkTS应用侧开发

// index.d.ts文件import testNapi from 'libentry.so';
import Constants from '../../common/constants/CommonConstants';@Entry
@Component
struct Index {@State imagePath: string = Constants.INIT_IMAGE_PATH;imageName: string = '';build() {Column() {...// button list, prompting the user to click the button to select the target image.Column() {...// multi-threads promise async buttonButton($r('app.string.async_promise_button_title')).width(Constants.FULL_PARENT).margin($r('app.float.button_common_margin')).onClick(() => {this.imageName = Constants.PROMISE_BUTTON_IMAGE;let promiseObj = testNapi.getImagePathAsyncPromise(this.imageName);promiseObj.then((result: string) => {this.imagePath = Constants.IMAGE_ROOT_PATH + result;})})...}...}...}
}import testNapi from 'libentry.so';
import Constants from '../../common/constants/CommonConstants';@Entry
@Component
struct Index {@State imagePath: string = Constants.INIT_IMAGE_PATH;imageName: string = '';build() {Column() {...// button list, prompting the user to click the button to select the target image.Column() {...// multi-threads promise async buttonButton($r('app.string.async_promise_button_title')).width(Constants.FULL_PARENT).margin($r('app.float.button_common_margin')).onClick(() => {this.imageName = Constants.PROMISE_BUTTON_IMAGE;let promiseObj = testNapi.getImagePathAsyncPromise(this.imageName);promiseObj.then((result: string) => {this.imagePath = Constants.IMAGE_ROOT_PATH + result;})})...}...}...}
}

        (2)Native侧开发

        导出Native接口:将Native接口导出到ArkTS侧,用于支撑ArkTS对象调用和模块编译构建。

// index.d.ts文件
export const getImagePathAsyncPromise: (imageName: string) => Promise<string>;

        execute回调:定义异步工作项的第一个回调函数,该函数在work子线程中执行,处理具体的业务逻辑。

// MultiThreads.cpp文件static void ExecuteFunc([[maybe_unused]] napi_env env, void *data) {// create producer threadthread producer(ProductElement, data);// the producer and consumer threads must be synchronized// otherwise, the complete operation is triggered to communicate with the ArkTS after the executeFunc is complete// the result is unpredictableproducer.join();// create consumer threadthread consumer(ConsumeElement, data);consumer.join();
}

        complete回调:定义异步工作项的第二个回调函数,该函数在ArkTS主线程中执行,将结果传递给ArkTS侧。

// MultiThreads.cpp文件static void CompleteFuncPromise(napi_env env, [[maybe_unused]] napi_status status, void *data) {// parse context dataContextData *contextData = static_cast<ContextData *>(data);// convert the calculation result of C++ sub-thread to the napi_value typenapi_value promiseArgs = nullptr;napi_status operStatus =napi_create_string_utf8(env, contextData->result.c_str(), contextData->result.length(), &promiseArgs);if (operStatus != napi_ok) {DeleteContext(env, contextData);return;}// the deferred and promise object are associated. the result is sent to ArkTS application through this interfaceoperStatus = napi_resolve_deferred(env, contextData->deferred, promiseArgs);if (operStatus != napi_ok) {DeleteContext(env, contextData);return;}// destroy data and release memoryDeleteContext(env, contextData);
}

        Native异步任务开发接口:解析ArkTS应用侧参数,使用napi_create_async_work创建异步工作项,并使用napi_queue_async_work将异步任务加入队列,等待调度执行。

// MultiThreads.cpp文件// promise async interface
static napi_value GetImagePathAsyncPromise(napi_env env, napi_callback_info info) {size_t paraNum = 1;napi_value paraArray[1] = {nullptr};// parse parametersnapi_status operStatus = napi_get_cb_info(env, info, &paraNum, paraArray, nullptr, nullptr);if (operStatus != napi_ok) {return nullptr;}napi_valuetype paraDataType = napi_undefined;operStatus = napi_typeof(env, paraArray[0], &paraDataType);if ((operStatus != napi_ok) || (paraDataType != napi_string)) {return nullptr;}// napi_value convert to char *constexpr size_t buffSize = 100;char strBuff[buffSize]{}; // char buffer for imageName stringsize_t strLength = 0;operStatus = napi_get_value_string_utf8(env, paraArray[0], strBuff, buffSize, &strLength);if ((operStatus != napi_ok) || (strLength == 0)) {return nullptr;}// defines context data. the memory will be released in CompleteFuncauto contextData = new ContextData;contextData->args = strBuff;// async resourcenapi_value asyncName = nullptr;string asyncStr = "async promise";operStatus = napi_create_string_utf8(env, asyncStr.c_str(), asyncStr.length(), &asyncName);if (operStatus != napi_ok) {DeleteContext(env, contextData);return nullptr;}// create async workoperStatus = napi_create_async_work(env, nullptr, asyncName, ExecuteFunc, CompleteFuncPromise,static_cast<void *>(contextData), &contextData->asyncWork);if (operStatus != napi_ok) {DeleteContext(env, contextData);return nullptr;}// add the async work to the queue and wait for schedulingoperStatus = napi_queue_async_work(env, contextData->asyncWork);if (operStatus != napi_ok) {DeleteContext(env, contextData);return nullptr;}// create promise objectnapi_value promiseObj = nullptr;operStatus = napi_create_promise(env, &contextData->deferred, &promiseObj);if (operStatus != napi_ok) {DeleteContext(env, contextData);return nullptr;}return promiseObj;
}

这篇关于使用Node-API进行异步任务开发的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1147363

相关文章

Python并行处理实战之如何使用ProcessPoolExecutor加速计算

《Python并行处理实战之如何使用ProcessPoolExecutor加速计算》Python提供了多种并行处理的方式,其中concurrent.futures模块的ProcessPoolExecu... 目录简介完整代码示例代码解释1. 导入必要的模块2. 定义处理函数3. 主函数4. 生成数字列表5.

Python中help()和dir()函数的使用

《Python中help()和dir()函数的使用》我们经常需要查看某个对象(如模块、类、函数等)的属性和方法,Python提供了两个内置函数help()和dir(),它们可以帮助我们快速了解代... 目录1. 引言2. help() 函数2.1 作用2.2 使用方法2.3 示例(1) 查看内置函数的帮助(

Linux脚本(shell)的使用方式

《Linux脚本(shell)的使用方式》:本文主要介绍Linux脚本(shell)的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述语法详解数学运算表达式Shell变量变量分类环境变量Shell内部变量自定义变量:定义、赋值自定义变量:引用、修改、删

Python实例题之pygame开发打飞机游戏实例代码

《Python实例题之pygame开发打飞机游戏实例代码》对于python的学习者,能够写出一个飞机大战的程序代码,是不是感觉到非常的开心,:本文主要介绍Python实例题之pygame开发打飞机... 目录题目pygame-aircraft-game使用 Pygame 开发的打飞机游戏脚本代码解释初始化部

Java使用HttpClient实现图片下载与本地保存功能

《Java使用HttpClient实现图片下载与本地保存功能》在当今数字化时代,网络资源的获取与处理已成为软件开发中的常见需求,其中,图片作为网络上最常见的资源之一,其下载与保存功能在许多应用场景中都... 目录引言一、Apache HttpClient简介二、技术栈与环境准备三、实现图片下载与保存功能1.

Python中使用uv创建环境及原理举例详解

《Python中使用uv创建环境及原理举例详解》uv是Astral团队开发的高性能Python工具,整合包管理、虚拟环境、Python版本控制等功能,:本文主要介绍Python中使用uv创建环境及... 目录一、uv工具简介核心特点:二、安装uv1. 通过pip安装2. 通过脚本安装验证安装:配置镜像源(可

LiteFlow轻量级工作流引擎使用示例详解

《LiteFlow轻量级工作流引擎使用示例详解》:本文主要介绍LiteFlow是一个灵活、简洁且轻量的工作流引擎,适合用于中小型项目和微服务架构中的流程编排,本文给大家介绍LiteFlow轻量级工... 目录1. LiteFlow 主要特点2. 工作流定义方式3. LiteFlow 流程示例4. LiteF

使用Python开发一个现代化屏幕取色器

《使用Python开发一个现代化屏幕取色器》在UI设计、网页开发等场景中,颜色拾取是高频需求,:本文主要介绍如何使用Python开发一个现代化屏幕取色器,有需要的小伙伴可以参考一下... 目录一、项目概述二、核心功能解析2.1 实时颜色追踪2.2 智能颜色显示三、效果展示四、实现步骤详解4.1 环境配置4.

使用jenv工具管理多个JDK版本的方法步骤

《使用jenv工具管理多个JDK版本的方法步骤》jenv是一个开源的Java环境管理工具,旨在帮助开发者在同一台机器上轻松管理和切换多个Java版本,:本文主要介绍使用jenv工具管理多个JD... 目录一、jenv到底是干啥的?二、jenv的核心功能(一)管理多个Java版本(二)支持插件扩展(三)环境隔

SQL中JOIN操作的条件使用总结与实践

《SQL中JOIN操作的条件使用总结与实践》在SQL查询中,JOIN操作是多表关联的核心工具,本文将从原理,场景和最佳实践三个方面总结JOIN条件的使用规则,希望可以帮助开发者精准控制查询逻辑... 目录一、ON与WHERE的本质区别二、场景化条件使用规则三、最佳实践建议1.优先使用ON条件2.WHERE用