三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践

马肤

温馨提示:这篇文章已超过459天没有更新,请注意相关的内容是否还可用!

摘要:本文介绍了三方库移植中的NAPI开发,重点阐述了异步调用机制。通过Callback和Promise两种实现方式,详细解释了NAPI在异步编程中的应用。文章指出,使用Callback和Promise可以有效地处理异步操作,提高代码的可读性和可维护性。本文还涉及了NAPI的其他特性和优势,为开发者在三方库移植过程中提供了有价值的参考和指导。

写在开头:

  • 本文在 三方库移植之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返回真正的结果

    • 异步工作项工作时序图:

      三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第1张

      • 原生方法被调用时,原生方法完成数据接收、数据类型转换、存入上下文数据,之后创建异步工作项
      • 异步工作项会加入调度队列,由异步工作线程池统一调度,原生方法返回空值(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 
                            #include 
                            #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: number, num2: number, callback:(result: number) => void): void;
                                /**
                                 * 
                                 *
                                 * @since 9
                                 * @syscap SystemCapability.Ability.AbilityRuntime.AbilityCore
                                 */
                            }
                            export default hellonapi;
                            

                            主线程:获取JS传入参数

                            获取JS传入参数在异步工作项工作时序图中位置,在图中用红框标记如下

                            三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第2张

                              // 获取并判断js参数类型
                              napi_valuetype valuetype0;
                              NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
                            // 使用napi_typeof接口进行参数类型的判断
                              napi_valuetype valuetype1;
                              NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
                            // 如果valuetype2调用的不是数据类型,则抛出异常“Wrong arguments. 2 numbers expected.”
                              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));
                            // 如果valuetype2调用的不是function类型(callback),则抛出异常“Callback function expected”
                              if (valuetype2 != napi_function) {
                                napi_throw_type_error(env, nullptr, "Callback function expected.");
                                return NULL;
                              }
                            
                            • 使用napi_typeof接口进行参数类型的判断
                            • NAPI_CALL()是用来调用NAPI中的API的。

                              主线程:初始化上下文数据

                              初始化上下文数据在异步工作项工作时序图中位置,在图中用红框标记如下

                              三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第3张

                              • 异步方法需要在不同线程中传递各种业务数据(上下文数据),就需要定义一个结构体保存这些被传递的信息。用于在主线程方法、Work线程、EventLoop线程之间传递数据。

                                struct 结构体名(也就是可选标记名){ 成员变量;};//使用分号;表示定义结束。

                                • 本示例定义的上下文数据包含:异步工作项对象、回调函数、2个参数(加数、被加数)、业务逻辑处理结果等4个属性。
                                  // 定义异步工作项上下文数据
                                  // 用户提供的上下文数据,用于在主线程方法、Work线程、EventLoop线程之间传递数据。
                                  struct AddonData {
                                    napi_async_work asyncWork = nullptr;   //异步工作对象asyncWork
                                    napi_ref callback = nullptr;           //回调函数callback
                                    double args[2] = {0};                  //2个输入参数
                                    double result = 0;                     //业务逻辑处理结果result(返回值)
                                  };
                                  
                                  • OpenHarmony的NAPI框架将ECMAScript标准中定义的Boolean、Null、Undefined、Number、BigInt、String、Symbol和Object八种数据类型和Function类型,都已统一封装为napi_value类型,故可如获取数据类型的参数一样获取Function类型的参数。

                                    Function是JavaScript提供的一种引用类型,通过Function类型创建Function对象。

                                    在JavaScript中,函数也是以对象的形式存在的,每个函数都是一个Function对象。

                                    • 定义好结构体后,接着我们将接收到的3个参数(加数、被加数、回调函数)转换存入上下文数据完成初始化上下文数据,
                                      • number类型的(加数、被加数)转换为double直接存入。
                                      • Function类型的参数(回调函数)怎么处理?不能直接存入napi_value类型。
                                        • 因为牵涉到NAPI对象生命周期管理问题。napi_value类型引用对象的生命周期在原生方法退出后结束,后面在work线程无法获取其值。
                                        • NAPI提供了一种生命期限长于原生方法的对象引用类型—— napi_ref,所以调用napi_create_reference()函数将接收到的napi_value类型的回调函数参数callback转换为napi_ref类型。napi_create_reference()函数定义如下:
                                          NAPI_EXTERN napi_status napi_create_reference(napi_env env,
                                                                                        napi_value value,
                                                                                        uint32_t initial_refcount,
                                                                                        napi_ref* result);
                                          

                                          参数说明:

                                          [in] env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。

                                          [in] value: 需要创建一个引用的napi_value对象

                                          [in] initial_refcount: 初始化引用次数。

                                          [out] result: 指针,指向新创建的napi_ref对象。 返回值:返回napi_ok表示转换成功,其他值失败。

                                          • napi_ref引用对象在原生方法退出后不自动回收,由用户管理napi_ref类型对象的生命周期。
                                            • 用户管理napi_ref类型对象的生命周期的方法有
                                              • napi_create_reference() : 将napi_value包装成napi_ref引用对象
                                              • napi_get_reference_value() : 从napi_ref引用对象中取得napi_value
                                              • napi_delete_reference() :删除napi_ref引用对象
                                              • 通过napi_create_reference()方法将napi_value创建一个napi_ref,这个napi_ref是可以跨作用域传递的,然后在需要用到的地方用napi_get_reference_value()方法将napi_ref还原为napi_value,用完后再用napi_delete_reference()方法删除引用对象以便释放相关内存资源。
                                                static napi_value addAsyncCallback(napi_env env, napi_callback_info info) {
                                                // NAPI定义API方法时的接收参数为(napi_env, napi_callback_info)
                                                // 其中napi_callback_info为上下文的信息。
                                                  size_t argc = 3;   // 有3个参数(`加数`、`被加数`、`回调函数`)到上下文中
                                                  napi_value args[3];
                                                  napi_value thisArg = nullptr;
                                                  NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));
                                                // NAPI提供了napi_get_cb_info()方法可从napi_callback_info中获取参数列表、this及其他数据。
                                                  ...
                                                  // 异步工作项上下文用户数据,传递到异步工作项的execute、complete中传递数据
                                                  // 创建结构体addonData用于保存各种需要在异步线程中传递的数据信息
                                                  auto addonData = new AddonData{
                                                      .asyncWork = nullptr,
                                                  };
                                                  // 将接收到的3个参数(`加数`、`被加数`、`回调函数`)传入用户自定义上下文数据
                                                  NAPI_CALL(env, napi_get_value_double(env, args[0], &addonData->args[0]));
                                                  // NAPI_CALL()是用来调用NAPI中的API的
                                                  // NAPI提供napi_get_value_double方法将JS类型double值转换为C++类型的double值
                                                  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));
                                                  //调用napi_create_reference()函数将接收到的napi_value类型的回调函数callback转换为napi_ref类型,将napi_value包装成napi_ref引用对象。并保存到asyncContext上下文数据中,以便后续在C++异步线程中能够回调该js fuction类型。
                                                  //参数解释如下
                                                     // env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可
                                                     // args[2]: 引用的napi_value对象 (加数和被加数)
                                                     // 1:初始化引用1次
                                                     // &addonData->callback: 指向新创建的napi_ref 对象(callback) 
                                                  ...
                                                }
                                                
                                                • NAPI_CALL()是用来调用NAPI中的API的。

                                                  主线程:创建异步工作项

                                                  创建异步工作项在异步工作项工作时序图中位置,在图中用红框标记如下

                                                  三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第4张

                                                  • 第一步:在创建异步工作项前,分别声明addExecuteCB、addAsyncCompleteCB这2个函数,分别用作于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)函数的execute、complete参数。
                                                  • 第二步:利用NAPI框架提供的napi_create_async_work()函数创建异步工作项,将addExecuteCB、addAsyncCompleteCB这2个函数存入上下文数据的asyncWork属性
                                                    // 业务逻辑处理函数,由异步work线程池统一调度
                                                    static void addExecuteCB(napi_env env, void *data) {
                                                    }
                                                    // 业务逻辑处理完成回调函数,在业务逻辑处理函数执行完成或取消后触发。
                                                    static void addAsyncCompleteCB(napi_env env, napi_status status, void *data) {
                                                    }
                                                    static napi_value addAsyncCallback(napi_env env, napi_callback_info info) {
                                                      ...
                                                        // 创建async work,创建成功后通过最后一个参数接收async work的handle
                                                        napi_value resourceName = nullptr;
                                                    // 根据UTF8编码格式的 C/C++字符串 创建一个 JS字符串对象.
                                                    // 传入的参数是Javascript值类型,被NAPI框架封装成统一的唯一类型——napi_value类型,为了能够进行计算,我们需要获取其对应在C/C++中的类型的值。将C/C++ utf8类型的值转为node_value类型,返回给JS代码
                                                        napi_create_string_utf8(env, "addCallback", NAPI_AUTO_LENGTH, &resourceName); //参数说明如下
                                                        //env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可
                                                        //addCallback:定义的上下文信息中的addCallback对象
                                                        //NAPI_AUTO_LENGTH:字符长度
                                                        //resourceName:创建的napi_value对象
                                                       // 异步方式依赖NAPI框架提供的napi_create_async_work()函数创建异步工作项
                                                       napi_create_async_work(env, nullptr, resourceName,   addExecuteCB  ,  addCallbackCompleteCB  ,   (void *)addonData,&addonData->asyncWork);
                                                       //参数说明如下
                                                       //env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。
                                                       //第二个参数是nullptr
                                                       //resourceName: 定义的上下文信息中的addCallback对象(异步资源标识符),主要用于async_hooks API暴露断言诊断信息。
                                                       //addExecuteCB:执行业务逻辑计算函数,由worker线程池调度执行。在该函数中执行IO、CPU密集型任务,不阻塞主线程。
                                                       //addCallbackCompleteCB: execute参数指定的函数执行完成或取消后,触发执行该函数。此函数在EventLoop线程中执行。
                                                       //(void *)addonData: 用户提供的上下文数据,用于传递数据。
                                                       //&addonData->asyncWork: 用于返回当前此处函数调用创建的异步工作项。 返回值:返回napi_ok表示转换成功,其他值失败。
                                                      ...
                                                    }
                                                    

                                                    主线程:异步工作项加入队列,等待调度

                                                    异步工作项加入队列,等待调度在异步工作项工作时序图中位置,在图中用红框标记如下

                                                    三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第5张

                                                    static napi_value addAsyncCallback(napi_env env, napi_callback_info info) {
                                                      ...
                                                      // 将刚创建的异步工作项(async work)加到队列,由work thread调度执行
                                                      napi_queue_async_work(env, addonData->asyncWork);
                                                      // 其中asyncWork是上下文数据中创建的异步工作对象,用于管理异步工作线程。
                                                      ...
                                                    }
                                                    

                                                    主线程:原生方法返回临时返回值

                                                    • 调用napi_queue_async_work()将异步工作项加入调度队列,由异步work线程池统一调度,原生方法返回空值退出。

                                                    • 用户在调用接口的时候,接口实现将异步执行任务,任务执行结果以参数的形式提供给用户注册的回调函数(callback),这些参数的第一个是 Error 或 undefined 类型,分别表示执行出错与正常。

                                                      static napi_value addAsyncCallback(napi_env env, napi_callback_info info) {
                                                        ...
                                                        // 为异步方法创建临时返回值,在此处原生方法临时返回值是一个空对象
                                                        napi_value result = 0;
                                                        // callback接口返回参数为void,用napi_get_null()构造一个空对象的返回值即可。
                                                        NAPI_CALL(env, napi_get_null(env, &result));
                                                        return result;
                                                      }
                                                      

                                                      work线程:执行业务逻辑、把计算结果写入上下文数据

                                                      执行业务逻辑、把计算结果写入上下文数据在异步工作项工作时序图中位置,在图中用红框标记如下

                                                      三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第6张

                                                      创建异步工作项前,声明了addExecuteCB这个函数,用作于napi_create_async_work()函数的execute参数。

                                                      • execute函数在异步工作项被调度后在work线程中执行

                                                        • 不阻塞主线程(不阻塞UI界面)
                                                        • 可执行IO、CPU密集型等任务。
                                                        • 执行业务逻辑:业务逻辑计算是一个简单的加法,并把计算结果存入上下文数据的result属性

                                                        • 把计算结果写入上下文数据:把execute函数的结构体指向上下文数据中结构体。

                                                          // 业务逻辑处理函数,由worker线程池调度执行。
                                                          static void addExecuteCB(napi_env env, void *data) {
                                                           // 把计算结果写入上下文数据,把addonData指向AddonData
                                                            AddonData *addonData = (AddonData *)data;
                                                            // 执行业务逻辑,
                                                            // 不阻塞主线程。此处是一个加法
                                                            addonData->result = addonData->args[0] + addonData->args[1];
                                                          }
                                                          

                                                          EventLoop线程:把上下文中的结果转为JS类型、调用JS回调函数

                                                          把上下文中的结果转为JS类型、调用JS回调函数在异步工作项工作时序图中位置,在图中用红框标记如下

                                                          三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第7张

                                                          • 创建异步工作项前,声明addAsyncCompleteCB这个函数,用作于napi_create_async_work()函数的complete参数。
                                                            • 第一步:addAsyncCompleteCB从接收到的上下文数据中获取结果,调用napi_call_function()方法执行JS回调函数返回数据给JS。
                                                            • 第二步: 释放(删除)过程中创建的napi_ref引用对象、异步工作项等对象。
                                                              // 业务逻辑处理完成回调函数,在业务逻辑处理函数执行完成或取消后触发,由EventLoop线程中执行。
                                                              static void addCallbackCompleteCB(napi_env env, napi_status status, void *data) {
                                                              //所有的接口调用返回一个napi_status类型的状态码,用来表明接口调用成功或者失败
                                                                AddonData *addonData = (AddonData *)data;
                                                                napi_value callback = nullptr;
                                                                //从napi_ref引用对象中取得napi_value
                                                                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;
                                                              }
                                                              
                                                              • NAPI框架提供了napi_call_function()函数供扩展Natvie代码(C/C++代码)调用JS函数,用于执行回调函数等场景。函数定义如下:

                                                                三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第8张

                                                                // Methods to work with Functions
                                                                NAPI_EXTERN napi_status napi_call_function(napi_env env,
                                                                                                           napi_value recv,
                                                                                                           napi_value func,
                                                                                                           size_t argc,
                                                                                                           const napi_value* argv,
                                                                                                           napi_value* result)
                                                                

                                                                参数说明:

                                                                [in] env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。

                                                                [in] recv: 传给被调用的this对象。

                                                                [in] func: 被调用的函数.

                                                                [in] argc: 函数参数个数(对应函数数组的长度)。

                                                                [in] argv: 函数参数数组.

                                                                [out] result: func函数执行的返回值。 返回值:返回napi_ok表示转换成功,其他值失败。

                                                                • 因对象生命周期管理问题,上下文数据的callback属性的类型为napi_ref,需要调用napi_get_reference_value()函数获取其指向的napi_value对象值才调用napi_call_function()函数。 napi_get_reference_value函数定义:

                                                                  三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第9张

                                                                  // Attempts to get a referenced value. If the reference is weak,
                                                                  // the value might no longer be available, in that case the call
                                                                  // is still successful but the result is nullptr.
                                                                  NAPI_EXTERN napi_status napi_get_reference_value(napi_env env, 
                                                                    napi_ref ref,  
                                                                    napi_value* result)
                                                                  

                                                                  参数说明:

                                                                  [in] env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。

                                                                  [in] ref: napi_ref对象

                                                                  [out] result: napi_ref引用的napi_value对象。 返回值:返回napi_ok表示转换成功,其他值失败。

                                                                  • 执行回调函数是为了在异步操作之后调用JS函数

                                                                  • napi_delete_reference()用于删除上下文数据中定义的napi_ref对象callback。napi_ref引用对象在原生方法退出后不自动回收,由用户管理napi_ref类型对象的生命周期。

                                                                  • napi_delete_async_work()用于删除异步工作线程,在异步调用的结尾释放async_work和相关业务数据的内存

                                                                    Callback异步接口总结

                                                                    以下图片为个人总结,可以在文末下载清晰的图片,下载之后推荐到[diagrams]。

                                                                    三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第10张

                                                                    Promise异步接口

                                                                    Promise异步接口示例代码

                                                                    hellonapi.cpp

                                                                    #include 
                                                                    #include
                                                                    #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];
                                                                      // addonData->result = addonData->args[0] + addonData[1];
                                                                    }
                                                                    static void addPromiseCompleteCB(napi_env env, napi_status status, void *data) {
                                                                      AddonData *addonData = (AddonData *)data;
                                                                      napi_value result = nullptr;
                                                                      napi_create_double(env, addonData->result, &result);
                                                                      napi_resolve_deferred(env, addonData->deferred, result);
                                                                         // 删除napi_ref对象
                                                                         if (addonData->callback != nullptr) {
                                                                         napi_delete_reference(env, addonData->callback);
                                                                         }
                                                                      // 删除异步工作项
                                                                      napi_delete_async_work(env, addonData->asyncWork);
                                                                      delete addonData;
                                                                      addonData = nullptr;
                                                                    }
                                                                    static napi_value addPromise(napi_env env, napi_callback_info info) {
                                                                      // 获取2个参数,值的类型是js类型(napi_value)
                                                                      size_t argc = 2;
                                                                      napi_value args[2];
                                                                      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;
                                                                      }
                                                                      // 创建promise
                                                                      napi_value promise = nullptr;
                                                                      napi_deferred deferred = nullptr;
                                                                      NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));
                                                                      // 异步工作项上下文用户数据,传递到异步工作项的execute、complete之间传递数据
                                                                      auto addonData = new AddonData{
                                                                          .asyncWork = nullptr,
                                                                          .deferred = deferred,
                                                                      };
                                                                      // 将接收到的参数传入
                                                                      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]));
                                                                      // 创建async work,创建成功后通过最后一个参数(addonData->asyncWork)返回async work的handle
                                                                      napi_value resourceName = nullptr;
                                                                      napi_create_string_utf8(env, "addAsyncCallback", NAPI_AUTO_LENGTH, &resourceName);
                                                                      napi_create_async_work(env, nullptr, resourceName, addExecuteCB, addPromiseCompleteCB, (void *)addonData,
                                                                                             &addonData->asyncWork);
                                                                      // 将刚创建的async work加到队列,由底层去调度执行
                                                                      napi_queue_async_work(env, addonData->asyncWork);
                                                                      // 原生方法返回promise
                                                                      return promise;
                                                                    }
                                                                    // napi_addon_register_func
                                                                    //2.指定模块注册对外接口的处理函数,具体扩展的接口在该函数中声明
                                                                    static napi_value registerFunc(napi_env env, napi_value exports)
                                                                    {
                                                                        static napi_property_descriptor desc[] = {
                                                                            { "addPromise", nullptr, addPromise, nullptr, nullptr, nullptr, napi_default, nullptr }
                                                                        };
                                                                        napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
                                                                        return exports;   
                                                                    }
                                                                    // 1.先定义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 },
                                                                    };
                                                                    //3.模块定义好后,调用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.addPromise(x, y).then(...)").margin(1).fontSize(20).onClick(() => {
                                                                            let num1 = 123, num2 = 456
                                                                            hellonapi.addPromise(num1, num2).then((result) => {
                                                                              prompt.showToast({ message: `hellonapi.addPromise(${num1}, ${num2}) = ${result}` })
                                                                            })
                                                                          })
                                                                        }
                                                                        .width('100%')
                                                                        .height('100%')
                                                                      }
                                                                    }
                                                                    

                                                                    @ohos.hellonapi.d.ts

                                                                    declare namespace hellonapi {
                                                                    	function addPromise(num1: number, num2: number): Promise;
                                                                        /**
                                                                         * 
                                                                         *
                                                                         * @since 9
                                                                         * @syscap SystemCapability.Ability.AbilityRuntime.AbilityCore
                                                                         */
                                                                    }
                                                                    export default hellonapi;
                                                                    

                                                                    创建Promise

                                                                    • Promise整体处理流程和Callback方式一样,在此小节只讨论Promise不同于Callback的部分

                                                                    • 首先创建Promise,NAPI框架中提供了napi_create_promise()函数用于创建Promise,调用该函数输出2个对象——deferred、promise。

                                                                      • promise用于原生方法返回,deferred传入异步工作项的上下文数据。complete函数中,应用napi_resolve_deferred()函数 或 napi_reject_deferred() 函数返回数据。

                                                                        函数定义如下:

                                                                        napi_status napi_create_promise(napi_env env,
                                                                                                        napi_deferred* deferred,
                                                                                                        napi_value* promise);
                                                                        

                                                                        参数说明:

                                                                        [in] env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。

                                                                        [out] deferred: 返回接收刚创建的deferred对象,关联Promise对象,后面使用napi_resolve_deferred() 或 napi_reject_deferred() 返回数据。

                                                                        [out] promise: 关联上面deferred对象的JS Promise对象 返回值:返回napi_ok表示转换成功,其他值失败。

                                                                        static napi_value addPromise(napi_env env, napi_callback_info info) {
                                                                          // 创建promise
                                                                          napi_value promise = nullptr;
                                                                          napi_deferred deferred = nullptr;
                                                                        // 创建promise对象。promise用于返回promise对象给js调用者
                                                                          NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));
                                                                          ...
                                                                          // 返回promise
                                                                          return promise;
                                                                        }
                                                                        

                                                                        初始化上下文数据

                                                                        • 定义一个上下文数据结构,用于保存和传递数据。Promise方式加上deferred属性。
                                                                          // 用户提供的上下文数据,在原生方法(初始化数据)、executeCB、completeCB之间传递数据
                                                                          struct AddonData {
                                                                            ...
                                                                            napi_deferred deferred = nullptr;
                                                                            double args[2] = {0};
                                                                            ...
                                                                          };
                                                                          static napi_value addPromise(napi_env env, napi_callback_info info) {
                                                                            // 获取2个参数,值的类型是js类型(napi_value)
                                                                            size_t argc = 2;
                                                                            napi_value args[2];
                                                                            napi_value thisArg = nullptr;
                                                                            NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));
                                                                            ...
                                                                            // 创建promise
                                                                            napi_value promise = nullptr;
                                                                            napi_deferred deferred = nullptr;
                                                                            NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));
                                                                            ...
                                                                            // 将接收到的参数传入
                                                                            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]));
                                                                            ...
                                                                          }
                                                                          
                                                                          • Callback方式在addCallback传入的是三个参数,Promise方式在addPromise传入的是两个参数。

                                                                            创建异步工作项

                                                                            • 同Callback方式一样在创建异步工作项前,分别声明2个函数,分别用作于napi_create_async_work()函数的execute、complete参数。

                                                                            • 异步工作项创建OK后,将其存入上下文数据的asyncWork属性,并调用napi_queue_async_work()将异步工作项加入调度队列,由异步work线程池统一调度,原生方法返回Promise对象退出。

                                                                              // 用户提供的上下文数据,在原生方法(初始化数据)、executeCB、completeCB之间传递数据
                                                                              struct AddonData {
                                                                                napi_async_work asyncWork = nullptr;
                                                                                napi_deferred deferred = nullptr;
                                                                                double args[2] = {0};
                                                                                double result = 0;
                                                                              };
                                                                              static napi_value addPromise(napi_env env, napi_callback_info info) {
                                                                                ...
                                                                                // 创建async work,创建成功后通过最后一个参数(addonData->asyncWork)用于后续在C++的异步线程中返回真正的计算结果
                                                                                napi_value resourceName = nullptr;
                                                                                napi_create_string_utf8(env, "addAsyncCallback", NAPI_AUTO_LENGTH, &resourceName);
                                                                                napi_create_async_work(env, nullptr, resourceName, addExecuteCB, addPromiseCompleteCB, (void *)addonData,
                                                                                                       &addonData->asyncWork);
                                                                                // 将刚创建的async work加到队列,由底层去调度执行
                                                                                napi_queue_async_work(env, addonData->asyncWork);
                                                                                // 原生方法返回promise
                                                                                return promise;
                                                                              }
                                                                              

                                                                              execute 回调处理

                                                                              此处完全同Callback方式,无需修改。

                                                                              // 业务逻辑处理函数,由worker线程池调度执行。
                                                                              static void addExecuteCB(napi_env env, void *data) {
                                                                                AddonData *addonData = (AddonData *)data;
                                                                                // 执行复杂计算,不阻塞主线程。此处用一个加法简单示意。
                                                                                addonData->result = addonData->args[0] + addonData->args[1];
                                                                              }
                                                                              

                                                                              complete 回调处理

                                                                              • 调用NAPI提供的napi_resolve_deferred() 或 napi_reject_deferred() 返回数据。之后释放过程中创建的napi_ref引用对象、异步工作项等对象。
                                                                                static void addPromiseCompleteCB(napi_env env, napi_status status, void *data) {
                                                                                  AddonData *addonData = (AddonData *)data;
                                                                                  napi_value result = nullptr;
                                                                                  napi_create_double(env, addonData->result, &result);
                                                                                  napi_resolve_deferred(env, addonData->deferred, result);
                                                                                     // 删除napi_ref对象
                                                                                     if (addonData->callback != nullptr) {
                                                                                     napi_delete_reference(env, addonData->callback);
                                                                                     }
                                                                                  // 删除异步工作项
                                                                                  napi_delete_async_work(env, addonData->asyncWork);
                                                                                  delete addonData;
                                                                                  addonData = nullptr;
                                                                                }
                                                                                

                                                                                规范异步接口

                                                                                hellonapi.cpp

                                                                                #include 
                                                                                #include
                                                                                #include "napi/native_node_api.h"
                                                                                #include "napi/native_api.h"
                                                                                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 void addPromiseCompleteCB(napi_env env, napi_status status, void *data) {
                                                                                  AddonData *addonData = (AddonData *)data;
                                                                                  napi_value result = nullptr;
                                                                                  napi_create_double(env, addonData->result, &result);
                                                                                  napi_resolve_deferred(env, addonData->deferred, result);
                                                                                  // 删除napi_ref对象
                                                                                  if (addonData->callback != nullptr) {
                                                                                    napi_delete_reference(env, addonData->callback);
                                                                                  }
                                                                                  // 删除异步工作项
                                                                                  napi_delete_async_work(env, addonData->asyncWork);
                                                                                  delete addonData;
                                                                                }
                                                                                static napi_value addAsync(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;
                                                                                  }
                                                                                  // 异步工作项上下文用户数据,传递到异步工作项的execute、complete中传递数据
                                                                                  auto addonData = new AddonData{
                                                                                      .asyncWork = nullptr,
                                                                                  };
                                                                                  if (argc == 2) {
                                                                                    // 创建promise
                                                                                    napi_value promise = nullptr;
                                                                                    napi_deferred deferred = nullptr;
                                                                                    NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));
                                                                                    addonData->deferred = deferred;
                                                                                    // 将接收到的参数传入
                                                                                    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]));
                                                                                    // 创建async work,创建成功后通过最后一个参数(addonData->asyncWork)返回async work的handle
                                                                                    napi_value resourceName = nullptr;
                                                                                    napi_create_string_utf8(env, "addPromise", NAPI_AUTO_LENGTH, &resourceName);
                                                                                    napi_create_async_work(env, nullptr, resourceName, addExecuteCB, addPromiseCompleteCB, (void *)addonData,
                                                                                                           &addonData->asyncWork);
                                                                                    // 将刚创建的async work加到队列,由底层去调度执行
                                                                                    napi_queue_async_work(env, addonData->asyncWork);
                                                                                    // 返回promise
                                                                                    return promise;
                                                                                  } else {
                                                                                    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;
                                                                                    }
                                                                                    // 将接收到的参数传入用户自定义上下文数据
                                                                                    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("addAsync", addAsync),
                                                                                  };
                                                                                  NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
                                                                                  return exports;
                                                                                }
                                                                                // 1.先定义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 },
                                                                                };
                                                                                //3.模块定义好后,调用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.addAsync(x, y, callback)").margin(10).fontSize(20).onClick(() => {
                                                                                        let num1 = 123, num2 = 456
                                                                                        hellonapi.addAsync(num1, num2, (result) => {
                                                                                          prompt.showToast({ message: `hellonapi.addAsync(${num1}, ${num2}) = ${result}` })
                                                                                        })
                                                                                      })
                                                                                      Button("hellonapi.addAsync(x, y).then(...)").margin(10).fontSize(20).onClick(() => {
                                                                                        let num1 = 123, num2 = 456
                                                                                        hellonapi.addAsync(num1, num2).then((result) => {
                                                                                          prompt.showToast({ message: `hellonapi.addAsync(${num1}, ${num2}) = ${result}` })
                                                                                        })
                                                                                      })
                                                                                    }
                                                                                    .width('100%')
                                                                                    .height('100%')
                                                                                  }
                                                                                }
                                                                                

                                                                                @ohos.hellonapi.d.ts

                                                                                declare namespace hellonapi {
                                                                                	function addAsync(num1: number, num2: number, callback:(result: number) => void): void;
                                                                                	function addAsync(num1: number, num2: number): Promise;
                                                                                    /**
                                                                                     * 
                                                                                     *
                                                                                     * @since 9
                                                                                     * @syscap SystemCapability.Ability.AbilityRuntime.AbilityCore
                                                                                     */
                                                                                }
                                                                                export default hellonapi;
                                                                                

                                                                                异步方法和同步方法.ts接口文件

                                                                                同步方法

                                                                                • 同步方法调用之后,将阻塞住JS线程直至获取到返回值。

                                                                                • 命名:动词+Sync或动词+名词+Sync

                                                                                • 格式:

                                                                                  • 无参:方法名()
                                                                                  • 有参:方法名Sync(必填参数[, 可选参数])
                                                                                  • 返回值:有

                                                                                  • 声明文件模板

                                                                                    declare namespace 模块名 
                                                                                    {
                                                                                    /**
                                                                                    * 方法描述
                                                                                    * @note 特殊说明
                                                                                    * @since (可选,方法支持版本与模块不一致时需标明)
                                                                                    * @sysCap 系统能力
                                                                                    * @devices 支持设备 (可选,支持设备类型与模块不一致时需标明)
                                                                                    * @param 参数 参数说明(可选,没有参数或参数用interface包含时不需要标明)
                                                                                    * @return 返回值说明(可选,没有返回值或返回值用interface包含时不需要标明)
                                                                                    */
                                                                                    // 无参
                                                                                    function 方法名Sync(): 返回值类型;
                                                                                    // 有参
                                                                                    function 方法名Sync(必填参数: 参数类型, options?: 可选参数类型): 返回值类型;
                                                                                    interface 可选参数类型 {
                                                                                    参数名: 参数类型;
                                                                                    }
                                                                                    }
                                                                                    export default 模块名;
                                                                                    
                                                                                    • 示例
                                                                                      declare namespace hellonapi {
                                                                                      	function add(num1: number, num2: number): number;
                                                                                          /**
                                                                                           * 
                                                                                           *
                                                                                           * @since 9
                                                                                           * @syscap SystemCapability.Ability.AbilityRuntime.AbilityCore
                                                                                           */
                                                                                      }
                                                                                      export default hellonapi;
                                                                                      

                                                                                      异步方法

                                                                                      • 异步方法调用整个过程不会阻碍调用者的工作。

                                                                                      • 命名:动词或动词+名词

                                                                                      • 格式:

                                                                                        • 无参:方法名([回调函数])
                                                                                        • 有参:方法名(必填参数[, 可选参数][, 回调函数])
                                                                                        • 返回值

                                                                                          • 若回调函数非空,则返回void
                                                                                          • 若回调函数为空,则返回Promise实例对象
                                                                                          • 声明文件模板

                                                                                            declare namespace 模块名 {
                                                                                            /**
                                                                                             * 方法描述
                                                                                             * @note 特殊说明
                                                                                             * @since (可选,方法支持版本与模块不一致时需标明)
                                                                                             * @sysCap 系统能力
                                                                                             * @devices 支持设备 (可选,支持设备类型与模块不一致时需标明)
                                                                                             * @param 参数 参数说明(可选,没有参数或参数用interface包含时不需要标明)
                                                                                             */
                                                                                            // 无参
                                                                                            function 方法名(callback: AsyncCallback): void;
                                                                                            function 方法名(): Promise;
                                                                                            // 有参
                                                                                            function 方法名(必填参数: 参数类型, callback: AsyncCallback): void;
                                                                                            function 方法名(必填参数: 参数类型, options: 可选参数类型, callback: AsyncCallback): void;
                                                                                            function 方法名(必填参数: 参数类型, options?: 可选参数类型): Promise;
                                                                                            interface 可选参数类型 {
                                                                                              参数名: 参数类型;
                                                                                            }
                                                                                            }
                                                                                            export default 模块名;
                                                                                            
                                                                                            • 示例
                                                                                              declare namespace hellonapi {
                                                                                              	function addAsync(num1: number, num2: number, callback:(result: number) => void): void;
                                                                                              	function addAsync(num1: number, num2: number): Promise;
                                                                                                  /**
                                                                                                   * 
                                                                                                   *
                                                                                                   * @since 9
                                                                                                   * @syscap SystemCapability.Ability.AbilityRuntime.AbilityCore
                                                                                                   */
                                                                                              }
                                                                                              export default hellonapi;	
                                                                                              

                                                                                              NAPI中的数据类型

                                                                                              • NAPI使用的数据类型和Node.js N-API保持一致。OpenHarmony的NAPI(Native API)组件是一套对外接口基于Node.js N-API规范开发的原生模块扩展开发框架。

                                                                                                通过查看foundation/arkui/napi/interfaces/inner_api/napi/native_node_api.h(编写NAPI拓展模块hellonapi.cpp需要包含的头文件)可以知道OpenHarmony基本的NAPI数据类型。

                                                                                                三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第11张

                                                                                                #include 中的js_native_api.h在ohos3.2beta3版本源码目录下路径为prebuilts/build-tools/common/nodejs/node-v12.18.4-linux-x64/include/node/js_native_api_types.h。

                                                                                                三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第12张

                                                                                                然后再分析prebuilts/build-tools/common/nodejs/node-v12.18.4-linux-x64/include/node/js_native_api_types.h和third_party/node/src/js_native_api_types.h内容的差别。

                                                                                                三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第13张

                                                                                                两者内容一致,可以推测OpenHarmony中基本的NAPI数据类型和Node.js N-API中的保持一致。而接口名方面,napi提供的接口名与三方Node.js一致,目前支持部分接口,详情见libnapi.ndk.json文件

                                                                                                // JSVM API types are all opaque pointers for ABI stability
                                                                                                // typedef undefined structs instead of void* for compile time type safety
                                                                                                typedef struct napi_env__* napi_env;
                                                                                                typedef struct napi_value__* napi_value;
                                                                                                typedef struct napi_ref__* napi_ref;
                                                                                                typedef struct napi_handle_scope__* napi_handle_scope;
                                                                                                typedef struct napi_escapable_handle_scope__* napi_escapable_handle_scope;
                                                                                                typedef struct napi_callback_info__* napi_callback_info;
                                                                                                typedef struct napi_deferred__* napi_deferred;
                                                                                                

                                                                                                预处理器发现 #include 指令后,就会寻找指令后面中的文件名,并把这个文件的内容包含到当前文件中。被包含文件中的文本将替换源代码文件中的#include 指令

                                                                                                • 以typedef struct napi_env__* napi_env为例,搜遍Node.js的源码都找不到napi_value__定义,那这个定义是什么意思呢?c语言中,允许定义一个没有定义的结构体的指针。所以napi_value其实就是一个一级指针。他不需要类型信息。

                                                                                                  typedef作用就是定义类型别名

                                                                                                  关于NAPI标准库中导出的符号列表

                                                                                                  • NAPI它基于Node.js N-API规范开发,因此可参考Node.js N-API了解NAPI标准库中符号列表。本文以3.2beta3源码中的node三方库为例,从third_party/node/README.OpenSource中可得知3.2beta3移植的node版本为14.19.1,因此可参考的Node.js N-API链接为14.19.1版本,如下:https://nodejs.org/docs/latest-v14.x/api/n-api.html

                                                                                                  • 标准库中导出的符号列表

                                                                                                    符号类型符号名备注
                                                                                                    FUNCnapi_module_register
                                                                                                    FUNCnapi_get_last_error_info
                                                                                                    FUNCnapi_throw
                                                                                                    FUNCnapi_throw_error
                                                                                                    FUNCnapi_throw_type_error
                                                                                                    FUNCnapi_throw_range_error
                                                                                                    FUNCnapi_is_error
                                                                                                    FUNCnapi_create_error
                                                                                                    FUNCnapi_create_type_error
                                                                                                    FUNCnapi_create_range_error
                                                                                                    FUNCnapi_get_and_clear_last_exception
                                                                                                    FUNCnapi_is_exception_pending
                                                                                                    FUNCnapi_fatal_error
                                                                                                    FUNCnapi_open_handle_scope
                                                                                                    FUNCnapi_close_handle_scope
                                                                                                    FUNCnapi_open_escapable_handle_scope
                                                                                                    FUNCnapi_close_escapable_handle_scope
                                                                                                    FUNCnapi_escape_handle
                                                                                                    FUNCnapi_create_reference
                                                                                                    FUNCnapi_delete_reference
                                                                                                    FUNCnapi_reference_ref
                                                                                                    FUNCnapi_reference_unref
                                                                                                    FUNCnapi_get_reference_value
                                                                                                    FUNCnapi_create_array
                                                                                                    FUNCnapi_create_array_with_length
                                                                                                    FUNCnapi_create_arraybuffer
                                                                                                    FUNCnapi_create_external
                                                                                                    FUNCnapi_create_external_arraybuffer
                                                                                                    FUNCnapi_create_object
                                                                                                    FUNCnapi_create_symbol
                                                                                                    FUNCnapi_create_typedarray
                                                                                                    FUNCnapi_create_dataview
                                                                                                    FUNCnapi_create_int32
                                                                                                    FUNCnapi_create_uint32
                                                                                                    FUNCnapi_create_int64
                                                                                                    FUNCnapi_create_double
                                                                                                    FUNCnapi_create_string_latin1
                                                                                                    FUNCnapi_create_string_utf8
                                                                                                    FUNCnapi_get_array_length
                                                                                                    FUNCnapi_get_arraybuffer_info
                                                                                                    FUNCnapi_get_prototype
                                                                                                    FUNCnapi_get_typedarray_info
                                                                                                    FUNCnapi_get_dataview_info
                                                                                                    FUNCnapi_get_value_bool
                                                                                                    FUNCnapi_get_value_double
                                                                                                    FUNCnapi_get_value_external
                                                                                                    FUNCnapi_get_value_int32
                                                                                                    FUNCnapi_get_value_int64
                                                                                                    FUNCnapi_get_value_string_latin1
                                                                                                    FUNCnapi_get_value_string_utf8
                                                                                                    FUNCnapi_get_value_uint32
                                                                                                    FUNCnapi_get_boolean
                                                                                                    FUNCnapi_get_global
                                                                                                    FUNCnapi_get_null
                                                                                                    FUNCnapi_get_undefined
                                                                                                    FUNCnapi_coerce_to_bool
                                                                                                    FUNCnapi_coerce_to_number
                                                                                                    FUNCnapi_coerce_to_object
                                                                                                    FUNCnapi_coerce_to_string
                                                                                                    FUNCnapi_typeof
                                                                                                    FUNCnapi_instanceof
                                                                                                    FUNCnapi_is_array
                                                                                                    FUNCnapi_is_arraybuffer
                                                                                                    FUNCnapi_is_typedarray
                                                                                                    FUNCnapi_is_dataview
                                                                                                    FUNCnapi_is_date
                                                                                                    FUNCnapi_strict_equals
                                                                                                    FUNCnapi_get_property_names
                                                                                                    FUNCnapi_set_property
                                                                                                    FUNCnapi_get_property
                                                                                                    FUNCnapi_has_property
                                                                                                    FUNCnapi_delete_property
                                                                                                    FUNCnapi_has_own_property
                                                                                                    FUNCnapi_set_named_property
                                                                                                    FUNCnapi_get_named_property
                                                                                                    FUNCnapi_has_named_property
                                                                                                    FUNCnapi_set_element
                                                                                                    FUNCnapi_get_element
                                                                                                    FUNCnapi_has_element
                                                                                                    FUNCnapi_delete_element
                                                                                                    FUNCnapi_define_properties
                                                                                                    FUNCnapi_call_function
                                                                                                    FUNCnapi_create_function
                                                                                                    FUNCnapi_get_cb_info
                                                                                                    FUNCnapi_get_new_target
                                                                                                    FUNCnapi_new_instance
                                                                                                    FUNCnapi_define_class
                                                                                                    FUNCnapi_wrap
                                                                                                    FUNCnapi_unwrap
                                                                                                    FUNCnapi_remove_wrap
                                                                                                    FUNCnapi_create_async_work
                                                                                                    FUNCnapi_delete_async_work
                                                                                                    FUNCnapi_queue_async_work
                                                                                                    FUNCnapi_cancel_async_work
                                                                                                    FUNCnapi_get_node_version
                                                                                                    FUNCnapi_get_version
                                                                                                    FUNCnapi_create_promise
                                                                                                    FUNCnapi_resolve_deferred
                                                                                                    FUNCnapi_reject_deferred
                                                                                                    FUNCnapi_is_promise
                                                                                                    FUNCnapi_run_script
                                                                                                    FUNCnapi_get_uv_event_loop

                                                                                                    Native API接口说明

                                                                                                    符号类型符号名备注
                                                                                                    FUNCnapi_run_script_path运行JavaScript文件

                                                                                                    三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第14张

                                                                                                    关于镜像文件的编译

                                                                                                    • 初次编译OpenHarmony标准系统镜像时,会完整的编译出boot_linux.img、config.cfg、MiniLoaderAll.bin、parameter.txt、ramdisk.img、resource.img、system.img、uboot.img、updater.img、userdata.img、vendor.img文件
                                                                                                    • 后面自己修改源码(不涉及内核源码)后编译,可以烧录自己编译的system.img、vendor.img、updater.img、userdata.img、ramdisk.img

                                                                                                      三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第15张

                                                                                                      为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

                                                                                                      《鸿蒙开发学习手册》:

                                                                                                      如何快速入门:https://qr21.cn/FV7h05

                                                                                                      1. 基本概念
                                                                                                      2. 构建第一个ArkTS应用
                                                                                                      3. ……

                                                                                                      三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第16张

                                                                                                      开发基础知识:https://qr21.cn/FV7h05

                                                                                                      1. 应用基础知识
                                                                                                      2. 配置文件
                                                                                                      3. 应用数据管理
                                                                                                      4. 应用安全管理
                                                                                                      5. 应用隐私保护
                                                                                                      6. 三方应用调用管控机制
                                                                                                      7. 资源分类与访问
                                                                                                      8. 学习ArkTS语言
                                                                                                      9. ……

                                                                                                      三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第17张

                                                                                                      基于ArkTS 开发:https://qr21.cn/FV7h05

                                                                                                      1. Ability开发
                                                                                                      2. UI开发
                                                                                                      3. 公共事件与通知
                                                                                                      4. 窗口管理
                                                                                                      5. 媒体
                                                                                                      6. 安全
                                                                                                      7. 网络与链接
                                                                                                      8. 电话服务
                                                                                                      9. 数据管理
                                                                                                      10. 后台任务(Background Task)管理
                                                                                                      11. 设备管理
                                                                                                      12. 设备使用信息统计
                                                                                                      13. DFX
                                                                                                      14. 国际化开发
                                                                                                      15. 折叠屏系列
                                                                                                      16. ……

                                                                                                      三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第18张

                                                                                                      鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

                                                                                                      三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第19张

                                                                                                      鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

                                                                                                      1.项目开发必备面试题

                                                                                                      2.性能优化方向

                                                                                                      3.架构方向

                                                                                                      4.鸿蒙开发系统底层方向

                                                                                                      5.鸿蒙音视频开发方向

                                                                                                      6.鸿蒙车载开发方向

                                                                                                      7.鸿蒙南向开发方向

                                                                                                      三方库移植之NAPI开发[4]异步调用:Callback&Promise,三方库移植之NAPI开发系列,异步调用详解——Callback与Promise实践 第20张


0
收藏0
文章版权声明:除非注明,否则均为VPS857原创文章,转载或复制请以超链接形式并注明出处。

相关阅读

  • 【研发日记】Matlab/Simulink自动生成代码(二)——五种选择结构实现方法,Matlab/Simulink自动生成代码的五种选择结构实现方法(二),Matlab/Simulink自动生成代码的五种选择结构实现方法详解(二)
  • 超级好用的C++实用库之跨平台实用方法,跨平台实用方法的C++实用库超好用指南,C++跨平台实用库使用指南,超好用实用方法集合,C++跨平台实用库超好用指南,方法与技巧集合
  • 【动态规划】斐波那契数列模型(C++),斐波那契数列模型(C++实现与动态规划解析),斐波那契数列模型解析与C++实现(动态规划)
  • 【C++】,string类底层的模拟实现,C++中string类的模拟底层实现探究
  • uniapp 小程序实现微信授权登录(前端和后端),Uniapp小程序实现微信授权登录全流程(前端后端全攻略),Uniapp小程序微信授权登录全流程攻略,前端后端全指南
  • Vue脚手架的安装(保姆级教程),Vue脚手架保姆级安装教程,Vue脚手架保姆级安装指南,Vue脚手架保姆级安装指南,从零开始教你如何安装Vue脚手架
  • 如何在树莓派 Raspberry Pi中本地部署一个web站点并实现无公网IP远程访问,树莓派上本地部署Web站点及无公网IP远程访问指南,树莓派部署Web站点及无公网IP远程访问指南,本地部署与远程访问实践,树莓派部署Web站点及无公网IP远程访问实践指南,树莓派部署Web站点及无公网IP远程访问实践指南,本地部署与远程访问详解,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南。
  • vue2技术栈实现AI问答机器人功能(流式与非流式两种接口方法),Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法探究,Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法详解
  • 发表评论

    快捷回复:表情:
    评论列表 (暂无评论,0人围观)

    还没有评论,来说两句吧...

    目录[+]

    取消
    微信二维码
    微信二维码
    支付宝二维码