鸿蒙OH实战开发:三方库移植之NAPI开发[4]异步调用:Callback&Promise


📚往期学习笔录📝:

📝 鸿蒙(HarmonyOS)北向开发知识点记录~
📝 鸿蒙(OpenHarmony)南向开发保姆级知识点汇总~
📝 鸿蒙应用开发与鸿蒙系统开发哪个更有前景?
📝 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~
📝 对于大前端开发来说,转鸿蒙开发究竟是福还是祸?
📝 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?
📝 记录一场鸿蒙开发岗位面试经历~
📝 持续更新中……


写在开头

  • 本文在 三方库移植之NAPI开发[1]—Hello OpenHarmony NAPI 的基础上修改hellonapi.cpp、index.ets,接着学习NAPI异步模型的Promise、Callback方式。
  • 本文共有三个示例,分别是Callback 异步接口示例、Promise 异步接口示例、规范异步接口示例。在本文末尾的资源中提供了这三个示例的源代码,读者可以下载在开发板上运行。
  • 开发基于最新的OpenHarmony3.2Beta3版本及API9,标准系统开发板为润和软件DAYU200。

NAPI异步方式实现原理

  • 同步方式和异步方式
    同步方式,所有的代码处理都在原生方法(主线程)中完成。
    异步方式,所有的代码处理在多个线程中完成。

  • 实现NAPI异步方法的步骤
    1)立即返回一个临时结果给js调用者
    2)另起线程完成异步业务逻辑的执行
    3)通过callback或promise返回真正的结果

  • 异步工作项工作时序图

  • 原生方法被调用时,原生方法完成数据接收数据类型转换存入上下文数据,之后创建异步工作项
  • 异步工作项会加入调度队列,由异步工作线程池统一调度,原生方法返回空值(Callback方式)或返回Promise对象(Promise方式)。
  • 异步方式依赖NAPI框架提供的napi_create_async_work()函数创建异步工作项
    napi_create_async_work()在foundation/arkui/napi/native_engine/native_node_api.cpp第71行
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: 传入接口调用者的环境,包含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表示转换成功,其他值失败。

napi_create_async_work里有两个回调:

  • execute
    • execute函数用于执行工作项的业务逻辑,异步工作项被调度后,该函数从上下文数据中获取输入数据,在worker线程中完成业务逻辑计算(不阻塞主线程)并将结果写入上下文数据。
    • 因为execute函数不在JS线程中,所以不允许execute函数调用napi的接口。业务逻辑的返回值可以返回到complete回调中处理。
  • complete
    • 业务逻辑处理execute函数执行完成或被取消后,触发EventLoop执行complete函数,complete函数从上下文数据中获取结果,转换为JS类型,调用JS回调函数通过Promise resolve()返回结果。
    • 可以调用napi的接口,将execute中的返回值封装成JS对象返回。此回调在JS线程中执行。
  • 管理简单的异步操作的方法还有这些
    • napi_delete_async_work(napi_env env, napi_async_work work)
      删除异步工作线程
    • napi_queue_async_work(napi_env env, napi_async_work work)
      将刚创建的异步工作项加到队列(排队),由底层去调度执行
    • napi_cancel_async_work(napi_env env, napi_async_work work)
      取消异步工作项

NAPI支持异步模型

  • OpenHarmony标准系统异步接口实现支持Promise方式和Callback方式。NAPI支持异步模型,提供了Promise、Callback方式。
  • 标准系统异步接口实现规范要求,若引擎开启Promise特性支持,则异步方法必须同时支持Callback方式和Promise方式。
    • 由应用开发者决定使用哪种方式,通过是否传递Callback函数区分异步方法是Callback方式还是Promise方式
    • 不传递Callback即为Promise方式(方法执行结果为Promise实例对象),否则为Callback方式
  • Promise、Callback 异步模型都是 OHOS 标准异步模型。
  • Callback异步模型
    • 用户在调用接口的时候,接口实现将异步执行任务
    • 任务执行结果以参数的形式提供给用户注册的回调函数,这些参数的第一个是 Error 或 undefined 类型,分别表示执行出错与正常。
  • Promise异步模型
    • 对象的状态不受外界影响;
    • 一旦状态改变了就不会再变,也就是说任何时候Promise都只有一种状态。
  • ES6原生提供了Promise对象,Promise是异步编程的一种解决方案,可以替代传统的解决方案回调函数和事件;
    • promise对象是一个异步操作的结果,提供了一些API使得异步执行可以按照同步的流表示出来,避免了层层嵌套的回调函数,保证了回调是以异步的方式进行调用的;
    • 用户在调用这些接口的时候,接口实现将异步执行任务,同时返回一个 Promise 对象,其代表异步操作的结果;
    • 在返回的结果的个数超过一个时,其以对象属性的形式返回。
  • ES6:全称ECMAScript 6.0。ECMAScript 是JavaScript语言的国际标准,JavaScript是ECMAScript的实现。

Callback 异步接口

Callback 异步接口示例代码

hellonapi.cpp文件

#include <string.h>
#include <stdio.h>
#include "napi/native_node_api.h"
#include "napi/native_api.h"

// 用户提供的上下文数据,在原生方法(初始化数据)、executeCB、completeCB之间传递数据
struct AddonData {
  napi_async_work asyncWork = nullptr;
  napi_deferred deferred = nullptr;
  napi_ref callback = nullptr;
  double args[2] = {0};
  double result = 0;
};

// 业务逻辑处理函数,由worker线程池调度执行。
static void addExecuteCB(napi_env env, void *data) {
  AddonData *addonData = (AddonData *)data;

  // 执行复杂计算,不阻塞主线程。此处用一个加法简单示意。
  addonData->result = addonData->args[0] + addonData->args[1];
}

// 业务逻辑处理完成回调函数,在业务逻辑处理函数执行完成或取消后触发,由EventLoop线程中执行。
static void addCallbackCompleteCB(napi_env env, napi_status status, void *data) {
  AddonData *addonData = (AddonData *)data;
  napi_value callback = nullptr;
  napi_get_reference_value(env, addonData->callback, &callback);
  napi_value undefined = nullptr;
  napi_get_undefined(env, &undefined);
  napi_value result = nullptr;
  napi_create_double(env, addonData->result, &result);
  napi_value callbackResult = nullptr;

  // 执行回调函数
  napi_call_function(env, undefined, callback, 1, &result, &callbackResult);

  // 删除napi_ref对象
  if (addonData->callback != nullptr) {
    napi_delete_reference(env, addonData->callback);
  }

  // 删除异步工作项
  napi_delete_async_work(env, addonData->asyncWork);
  delete addonData;
}

static napi_value addCallback(napi_env env, napi_callback_info info) {
  // 获取3个参数,值的类型是js类型(napi_value)
  size_t argc = 3;
  napi_value args[3];
  napi_value thisArg = nullptr;
  NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));

  // 获取并判断js参数类型
  napi_valuetype valuetype0;
  NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
  napi_valuetype valuetype1;
  NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
  if (valuetype0 != napi_number || valuetype1 != napi_number) {
    napi_throw_type_error(env, nullptr, "Wrong arguments. 2 numbers expected.");
    return NULL;
  }

  napi_valuetype valuetype2;
  NAPI_CALL(env, napi_typeof(env, args[2], &valuetype2));
  if (valuetype2 != napi_function) {
    napi_throw_type_error(env, nullptr, "Callback function expected.");
    return NULL;
  }

  // 异步工作项上下文用户数据,传递到异步工作项的execute、complete中传递数据
  auto addonData = new AddonData{
      .asyncWork = nullptr,
  };

  // 将接收到的参数传入用户自定义上下文数据
  NAPI_CALL(env, napi_get_value_double(env, args[0], &addonData->args[0]));
  NAPI_CALL(env, napi_get_value_double(env, args[1], &addonData->args[1]));
  NAPI_CALL(env, napi_create_reference(env, args[2], 1, &addonData->callback));

  // 创建async work,创建成功后通过最后一个参数接收async work的handle
  napi_value resourceName = nullptr;
  napi_create_string_utf8(env, "addCallback", NAPI_AUTO_LENGTH, &resourceName);
  napi_create_async_work(env, nullptr, resourceName, addExecuteCB, addCallbackCompleteCB, (void *)addonData,
                         &addonData->asyncWork);

  // 将刚创建的async work加到队列,由底层去调度执行
  napi_queue_async_work(env, addonData->asyncWork);

  // 原生方法返回空对象
  napi_value result = 0;
  NAPI_CALL(env, napi_get_null(env, &result));
  return result;
}

// napi_addon_register_func
static napi_value registerFunc(napi_env env, napi_value exports) {
  static napi_property_descriptor desc[] = {
      DECLARE_NAPI_FUNCTION("addCallback", addCallback),
  };
  NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
  return exports;
}

// 定义napi_module,指定当前NAPI模块对应的模块名
//以及模块注册对外接口的处理函数,具体扩展的接口在该函数中声明
// nm_modname: 模块名称,对应eTS代码为import nm_modname from '@ohos.ohos_shared_library_name'
//示例对应eTS代码为:import hellonapi from '@ohos.hellonapi'
static napi_module hellonapiModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = registerFunc, // 模块对外接口注册函数
    .nm_modname = "hellonapi",  // 自定义模块名
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

// 模块定义好后,调用NAPI提供的模块注册函数napi_module_register(napi_module* mod)函数注册到系统中。
// register module,设备启动时自动调用此constructor函数,把模块定义的模块注册到系统中
extern "C" __attribute__((constructor)) void hellonapiModuleRegister()
{
    napi_module_register(&hellonapiModule);
}

index.ets

import prompt from '@system.prompt';
import hellonapi from '@ohos.hellonapi'

@Entry
@Component
struct TestAdd {
  build() {
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
      Button("hellonapi.addCallback(x, y, callback)").margin(10).fontSize(20).onClick(() => {
        let num1 = 123, num2 = 456
        hellonapi.addCallback(num1, num2, (result) => {
          prompt.showToast({ message: `hellonapi.addCallback(${num1}, ${num2}) = ${result}` })
        })
      })
    }
    .width('100%')
    .height('100%')
  }
}

@ohos.hellonapi.d.ts

declare namespace hellonapi {

	function addCallback(num1: n
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值