27d56e10创建于 2025年11月27日历史提交

Customizing Asynchronous Operations Using Node-API

Introduction

Node-API provides APIs for customizing asynchronous (async for short) operations to handle time-consuming tasks that may block event loops while maintaining quick response and high performance of applications.

Basic Concepts

Async operations are used to complete I/O-intensive or compute-intensive tasks, which usually need to be executed without blocking the main thread. Before you get started, understand the following concepts:

  • Async model: Node-API provides APIs that implement async operations using a promise or a callback. Promise is a programming model based on future values. It allows results of async operations to be encapsulated in objects and called in a chain. Callback is a traditional async programming mode. It uses callback functions to process async operation results.
  • Temporary result: When a native method (Node-API) is called, it immediately returns a temporary result to the ArkTS caller. The temporary result is usually a flag indicating an async operation being performed or a handle for subsequent processing of an async operation result.
  • Callback/Promise: When an async operation is complete, the result is returned to the ArkTS caller through a callback function or a promise object. This allows the processing of the subsequent logic after the async operation is complete.

Available APIs

The following table lists the APIs provided by the Node-API module for customizing async operations. You can use these APIs to implement ArkTS callbacks and manage the resource lifecycle in C/C++. These APIs help implement complex async operations and effective interaction with ArkTS. The following table lists the use cases of these APIs.

API Description
napi_async_init, napi_async_destroy Creates/Destroys an async context. You can use these APIs to handle time-consuming tasks, such as file I/O operations and network requests, without blocking the main thread. You can use napi_async_init to create an async context for executing the task, and use napi_async_destroy after the task is complete to destroy and release related resources.
napi_make_callback Executes an ArkTS callback function in an async context and returns the operation result to ArkTS.
napi_open_callback_scope, napi_close_callback_scope Opens/Closes a callback scope. You can use these APIs to execute ArkTS code and manage its context during the async operation.

Example

If you are just starting out with Node-API, see Node-API Development Process. The following demonstrates only the C++ and ArkTS code involved in the APIs for customizing async operations.

napi_async_init and napi_async_destroy

Use napi_async_init to create an async context, and use napi_async_destroy to destroy an async context. Note that these APIs do not support capabilities related to async_hook.

napi_make_callback

Use napi_make_callback to call and execute an ArkTS callback after an async operation is complete.

napi_open_callback_scope, napi_close_callback_scope

Use napi_open_callback_scope to create a scope for the callback, and then use napi_close_callback_scope to close the scope after the asynchronous operation is complete.

CPP code:

#include "napi/native_api.h"

static constexpr int INT_ARG_2 = 2; // Input parameter index.
static constexpr int INT_ARG_3 = 3; // Input parameter index

static napi_value AsynchronousWork(napi_env env, napi_callback_info info)
{
    // Initialize an array to hold four parameters.
    size_t argc = 4;
    napi_value args[4] = {nullptr};
    // Obtain parameters from the callback information.
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    // Extract resources, receiver objects, and functions from the parameters.
    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];
    // Obtain the function type.
    napi_valuetype funcType;
    napi_typeof(env, func, &funcType);
    // Create a string named "test".
    napi_value resourceName = nullptr;
    napi_create_string_utf8(env, "test", NAPI_AUTO_LENGTH, &resourceName);
    // Initialize the async context.
    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;
    }
    // Open a callback scope.
    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;
    }
    // Invoke the callback function defined.
    napi_value result = nullptr;
    if (funcType == napi_function) {
        napi_make_callback(env, context, recv, func, 1, argv, &result);
    } else {
        napi_throw_error(env, nullptr, "Unexpected argument type");
        return nullptr;
    }
    // Close the callback scope.
    status = napi_close_callback_scope(env, scope);
    if (status != napi_ok) {
        napi_throw_error(env, nullptr, "napi_close_callback_scope fail");
        return nullptr;
    }
    // Destroy the async context.
    napi_async_destroy(env, context);
    return result;
}

API declaration:

index.d.ts

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

ArkTS code:

Modules to import:

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

Test code:

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);
  // ···
}

To print logs in the native CPP, add the following information to the CMakeLists.txt file and add the header file by using #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)