使用Node-API进行自定义异步操作相关开发
=======================

#### 简介

使用Node-API的自定义异步操作功能，可以使ArkTS更灵活高效地处理那些可能阻塞事件循环的长时间运行任务，同时保持应用的响应性和性能。

#### 基本概念

Node-API支持异步操作，这有助于处理IO密集型或计算密集型任务。这些任务通常需要非阻塞的执行方式，以避免阻塞主线程。以下是一些关于自定义异步操作的基本概念：

*   **异步模型：** Node-API支持异步模型，提供了Promise和Callback两种方式来实现异步操作。Promise是一种基于未来值的编程模型，它允许开发者将异步操作的结果封装在一个对象中，并通过链式调用的方式处理异步操作的结果。Callback则是一种传统的异步编程方式，通过回调函数来处理异步操作的结果。
*   **临时结果：** 当原生方法（即Node-API代码）被调用时，它会立即返回一个临时结果给ArkTS调用者。这个临时结果通常是一个表示异步操作正在进行中的标志，或者是用于后续处理异步操作结果的句柄。
*   **回调或Promise：** 当异步操作完成后，结果会通过回调函数或Promise对象返回给ArkTS调用者。这样，ArkTS代码就可以在异步操作完成后继续执行后续的逻辑。

#### 场景和功能介绍

这些Node-API接口可以在Node-API模块中执行异步操作、进行ArkTS回调以及管理相关资源的生命周期。通过使用这些函数，可以有效地与ArkTS环境进行交互，并实现复杂的异步操作。它们的使用场景如下：

| 接口 | 描述 |
| :-- | :-- |
| napi\_async\_init、napi\_async\_destroy | 用于创建和销毁异步资源上下文环境。这些函数可以用于处理长时间运行的异步操作，例如文件I/O操作、网络请求等。在这些情况下，创建异步资源上下文环境，执行必要的异步任务，然后销毁资源以释放相关的资源和内容。 |
| napi\_make\_callback | 用于在异步资源上下文环境中执行ArkTS回调函数。在处理异步操作的结果后，将结果传递回ArkTS代码。 |
| napi\_open\_callback\_scope、napi\_close\_callback\_scope | 用于创建和关闭回调作用域。在异步操作期间执行ArkTS代码并管理其上下文。 |

#### 使用示例

Node-API接口开发流程参考[使用Node-API实现跨语言交互开发流程](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/use-napi-process)，本文仅对接口对应C++及ArkTS相关代码进行展示。

#### \[h2\]napi\_async\_init、napi\_async\_destroy

在需要管理异步资源上下文环境的创建和销毁时，可以使用napi\_async\_init和napi\_async\_destroy来管理这些环境。需要注意的是，这些函数不支持与async\_hook相关的能力，所以在使用时需要注意可能存在的一些限制。

#### \[h2\]napi\_make\_callback

在编写Node-API模块时，异步操作完成后需调用ArkTS回调函数。可使用napi\_async\_init创建异步资源上下文，再使用napi\_make\_callback执行ArkTS回调函数。

#### \[h2\]napi\_open\_callback\_scope、napi\_close\_callback\_scope

在需要创建一个回调作用域来确保异步操作期间ArkTS环境仍然可用时，可以使用napi\_open\_callback\_scope创建回调作用域，然后在异步操作完成后使用napi\_close\_callback\_scope关闭该作用域。

cpp部分代码

#include "napi/native\_api.h"

static constexpr int INT\_ARG\_2 = 2; // 入参索引
static constexpr int INT\_ARG\_3 = 3; // 入参索引

static napi\_value AsynchronousWork(napi\_env env, napi\_callback\_info info)
{
    // 接受四个参数
    size\_t argc = 4;
    napi\_value args\[4\] = {nullptr};
    // 从回调信息中获取参数
    napi\_get\_cb\_info(env, info, &argc, args, nullptr, nullptr);
    // 提取参数中的资源、接收器对象和函数
    napi\_value resource = args\[0\];
    napi\_value recv = args\[1\];
    napi\_value func = args\[INT\_ARG\_2\];
    napi\_value argv\[1\] = {nullptr};
    argv\[0\] = args\[INT\_ARG\_3\];
    // 获取函数的类型
    napi\_valuetype funcType;
    napi\_typeof(env, func, &funcType);
    // 创建一个资源名称为"test"的字符串
    napi\_value resourceName = nullptr;
    napi\_create\_string\_utf8(env, "test", NAPI\_AUTO\_LENGTH, &resourceName);
    // 初始化异步上下文
    napi\_async\_context context;
    napi\_status status = napi\_async\_init(env, resource, resourceName, &context);
    if (status != napi\_ok) {
        napi\_throw\_error(env, nullptr, "napi\_async\_init fail");
        return nullptr;
    }
    // 打开回调作用域
    napi\_callback\_scope scope = nullptr;
    status = napi\_open\_callback\_scope(env, resource, context, &scope);
    if (status != napi\_ok) {
        napi\_async\_destroy(env, context);
        napi\_throw\_error(env, nullptr, "napi\_open\_callback\_scope fail");
        return nullptr;
    }
    // 调用回调函数
    napi\_value result = nullptr;
    if (funcType == napi\_function) {
        napi\_make\_callback(env, context, recv, func, 1, argv, &result);
    } else {
        napi\_async\_destroy(env, context);
        napi\_close\_callback\_scope(env, scope);
        napi\_throw\_error(env, nullptr, "Unexpected argument type");
        return nullptr;
    }
    // 关闭回调作用域
    status = napi\_close\_callback\_scope(env, scope);
    if (status != napi\_ok) {
        napi\_throw\_error(env, nullptr, "napi\_close\_callback\_scope fail");
        return nullptr;
    }
    // 销毁异步上下文
    napi\_async\_destroy(env, context);
    return result;
}

接口声明

index.d.ts

export const asynchronousWork: (object: Object, obj: Object, fun: Function, num: number) => number | undefined;

ArkTS侧示例代码

导入模块

import { hilog } from '@kit.PerformanceAnalysisKit';
import testNapi from 'libentry.so';
import { process } from '@kit.ArkTS';

测试代码

try {
  hilog.info(0x0000, 'testTag', 'Test Node-API asynchronousWork: %{public}d',
    testNapi.asynchronousWork({}, process.ProcessManager, (num: number) => {
      return num;
    }, 123));
  // ···
} catch (error) {
  hilog.error(0x0000, 'testTag', 'Test Node-API asynchronousWork error: %{public}s', error.message);
  // ···
}

以上代码如果要在native cpp中打印日志，需在CMakeLists.txt文件中添加以下配置信息（并添加头文件：#include "hilog/log.h"）：

// CMakeLists.txt
add\_definitions( "-DLOG\_DOMAIN=0xd0d0" )
add\_definitions( "-DLOG\_TAG=\\"testTag\\"" )
target\_link\_libraries(entry PUBLIC libace\_napi.z.so libhilog\_ndk.z.so)