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

news2024/11/15 1:42:38

一、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 button
        Button($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 thread
    thread 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 unpredictable
    producer.join();
    // create consumer thread
    thread 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 data
    ContextData *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 undefined
    napi_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 type
    napi_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 application
    napi_value callBackResult = nullptr;
    (void)napi_call_function(env, undefined, callBack, 1, &callBackArgs, &callBackResult);
    // destroy data and release memory
    DeleteContext(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 parameters
    napi_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 string
    size_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 CompleteFunc
    auto 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 resource
    napi_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 work
    operStatus = 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 scheduling
    operStatus = 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 button
        Button($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 button
        Button($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 thread
    thread 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 unpredictable
    producer.join();
    // create consumer thread
    thread 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 data
    ContextData *contextData = static_cast<ContextData *>(data);
    // convert the calculation result of C++ sub-thread to the napi_value type
    napi_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 interface
    operStatus = napi_resolve_deferred(env, contextData->deferred, promiseArgs);
    if (operStatus != napi_ok) {
        DeleteContext(env, contextData);
        return;
    }
    // destroy data and release memory
    DeleteContext(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 parameters
    napi_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 string
    size_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 CompleteFunc
    auto contextData = new ContextData;
    contextData->args = strBuff;
    // async resource
    napi_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 work
    operStatus = 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 scheduling
    operStatus = napi_queue_async_work(env, contextData->asyncWork);
    if (operStatus != napi_ok) {
        DeleteContext(env, contextData);
        return nullptr;
    }
    // create promise object
    napi_value promiseObj = nullptr;
    operStatus = napi_create_promise(env, &contextData->deferred, &promiseObj);
    if (operStatus != napi_ok) {
        DeleteContext(env, contextData);
        return nullptr;
    }
    return promiseObj;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2113887.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TCP远程命令执行

目录 一. 命令集 二. 命令执行模块实现 三. 服务端模块实现 四. 服务端调用模块实现 五. 客户端模块实现 六. 效果展示 此篇教大家如何利用TCP进行远程命令执行。 一. 命令集 将值得信任的命令放进一个txt文件中&#xff0c;执行命令时&#xff0c;就去这…

英语每日一段 195

Promising economic indicators won’t instantly reverse the lingering impact of hard times for millions of families, workplace culture expert Jessica Kriegel said. “Perception and reality are sometimes aligned and sometimes not,” Kriegel told Newsweek. “…

这才是程序猿梦想的终端,赶快动手搞起来

文章目录 目标资源列表安装iTerm2安装oh-my-zsh安装颜色主题查找配置文件将配置内容复制到本地设置iTerm2 安装NERD FONTS下载字体安装设置iTerm2 安装PowerLevel10k修改.zshrc重新加载配置 安装插件下载[语法高亮](#syntaxhighlighting)下载[命令提示](#autosuggestions)配置插…

git的使用和gdb工具

1.git的使用 首先现在gitee上新建一个仓库 然后复制克隆链接到本地仓库 在本地仓库中&#xff0c;我们可以用git status查看仓库状态 我们要提交代码就是要三步 git add 文件名 git commit -m "写提交的日志" git push 将代码上传到远端仓库 然后你就完成一次提…

AIOT人工智能物联网六大场景

AIOT&#xff08;人工智能物联网&#xff09;融合了人工智能技术和物联网技术&#xff0c;实现了设备之间的智能互联和数据交互&#xff0c;在多个场景中都有广泛的应用。以下是一些主要的AIOT场景&#xff1a; 一、智能交通 智能汽车&#xff1a; 自动驾驶是AIOT在智能交通领…

tolower/toupper 函数讲解

目录 1.函数介绍 2.示例如下&#xff1a; 方源一把抓住VS2022&#xff0c;又是顷刻炼化&#xff01;&#xff1f; 1.函数介绍 C语言中提供了两种函数用于字符大小的转换 tolower可以将大写字符转小写字符&#xff0c;toupper可以将小写字符转大写字符 tolower函数与touppe…

设计并用Java实现一个简易的规则引擎

文章目录 前言正文一、代码结构二、核心代码2.1 上下文数据接口 ContextData.java2.2 规则接口 Rule.java2.3 规则引擎接口 RuleEngine.java2.4 规则引擎上下文类 RuleEngineContext.java2.5 规则引擎默认实现类 DefaultRuleEngine.java2.6 执行时出错监听器接口 ExecuteOnErro…

[mysql]SQL语言的规则和规范

规则 是什么呢&#xff0c;规则就是我们最基本&#xff0c;每时每刻都要遵守的比如人行道靠右&#xff0c;不能逆行&#xff0c; 规范 呢就是锦上添花&#xff0c;如果你不这么做&#xff0c;是不那么道德&#xff0c;不那么好的&#xff0c;就像小学生见到老师要问好&#…

【秋招笔试题】浇水

题解&#xff1a;离散化之后差分数组&#xff0c;注意左闭右闭区间的处理&#xff0c;将点和线段都抽象成点 #include <iostream> #include <vector> #include <set> #include <algorithm>using namespace std;const int MAXN 3000000 5; const int…

基于Spring Boot的火车订票管理系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;JAVA语言 Spring Boot框架 工具&#xff1a;IDEA/Eclipse、Navicat、Tomcat 系统展示 首页 管理…

方正畅享新闻采编系统 binary.do SQL注入漏洞分析复现

漏洞简介 binary.do接口的TableName参数对传入的数据没有充足的校验&#xff0c;导致该接口存在SQL注入漏洞&#xff0c;未授权的攻击者可获取数据库敏感信息。资产测绘搜索语句 hunter&#xff1a;web.body"/newsedit/newsedit/" 路由与鉴权分析 通过分析web.xml配置…

ubuntu 安装python3 教程

本篇教程,主要介绍如何在Ubuntu上安装python3教程。 1、查看是否有python 在安装前,首先看看自己系统上,是否存在python环境,可能有些系统,默认就安装过python,如果已经有python了,可以直接跳过安装教程。 2、安装步骤 apt update && apt install -y python3 p…

【知识分享】MQTT实战-使用mosquitto客户端连接emqx服务器

一、简介 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是一种轻量级的、基于发布/订阅模式的通信协议&#xff0c;旨在实现物联网设备之间的低带宽、高延迟的通信。MQTT协议设计简洁&#xff0c;使用TCP/IP协议进行通信&#xff0c;适用于各种网络环境&am…

OpenGL——着色器画一个点

一、 绘制 在窗口中间画一个像素点&#xff1a; #include <GL/glew.h> #include <GLFW/glfw3.h> #include <iostream>using namespace std;#define numVAOs 1GLuint renderingProgram; GLuint vao[numVAOs];GLuint createShaderProgram () {const char *v…

基于canal的Redis缓存双写

canal地址&#xff1a;alibaba/canal: 阿里巴巴 MySQL binlog 增量订阅&消费组件 (github.com)https://github.com/alibaba/canal 1. 准备 1.1 MySQL 查看主机二进制日志 show master status 查看binlog是否开启 show variables like log_bin 授权canal连接MySQL账号 …

有限自动机例题

答案&#xff1a;A 解析&#xff1a; 从图中可以看出从1出发&#xff0c;有一个a的闭环&#xff0c;可以多次重复a&#xff0c;因此选项A不正确 选项B&#xff0c;如果有b&#xff0c;必然经过a回去&#xff0c;不可能出现连续的b 选项C&#xff0c;可以从图中看出&#xf…

前端学习-day14

文章目录 01-媒体查询02-媒体查询-书写顺序03-媒体查询04-媒体查询-link引入06-Bootstrap-使用07-Bootstrap-栅格系统08-Bootstrap-按钮样式09-Bootstrap-表格样式10-bootstrap组件11-bootstrap字体图标alloyTeam项目index.htmlindex.less 01-媒体查询 <!DOCTYPE html> …

数学建模算法汇总(全网最全,含matlab案例代码)

数学建模常用的算法分类 全国大学生数学建模竞赛中&#xff0c;常见的算法模型有以下30种&#xff1a; 最小二乘法数值分析方法图论算法线性规划整数规划动态规划贪心算法分支定界法蒙特卡洛方法随机游走算法遗传算法粒子群算法神经网络算法人工智能算法模糊数学时间序列分析马…

一文梳理RAG(检索增强生成)的现状与挑战

一 RAG简介 大模型相较于过去的语言模型具备更加强大的能力&#xff0c;但在实际应用中&#xff0c;例如在准确性、知识更新速度和答案透明度方面&#xff0c;仍存在不少问题&#xff0c;比如典型的幻觉现象。因此&#xff0c;检索增强生成 (Retrieval-Augmented Generation, …

Learn ComputeShader 09 Night version lenses

这次将要制作一个类似夜视仪的效果 第一步就是要降低图像的分辨率&#xff0c; 这只需要将id.xy除上一个数字然后再乘上这个数字 可以根据下图理解&#xff0c;很明显通过这个操作在多个像素显示了相同的颜色&#xff0c;并且很多像素颜色被丢失了&#xff0c;自然就会有降低分…