Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing Asynchronous Tasks with Node-API Using napi_create_async_work

Tech 1

The Node-API functon napi_create_async_work facilitates the execution of tasks in background threads, allowing long-running operations to avoid blocking the main thread. This approach involves defining two callback functions: one for background execution and another for completion handling on the main thread.

Promise-Based Example

Define a structure to hold context data for the asynchronous operation.

struct TaskContext {
    napi_async_work asyncTask = nullptr;
    napi_deferred deferredPromise = nullptr;
    double input = 0;
    double output = 0;
};

Create the asynchronous work, queue it, and return a promise.

napi_value StartAsyncTask(napi_env env, napi_callback_info info) {
    size_t paramCount = 1;
    napi_value params[1];
    napi_get_cb_info(env, info, &paramCount, params, nullptr, nullptr);
    auto ctx = new TaskContext();
    napi_get_value_double(env, params[0], &ctx->input);
    napi_create_promise(env, &ctx->deferredPromise, &promise);
    napi_value taskName;
    napi_create_string_utf8(env, "asyncTask", NAPI_AUTO_LENGTH, &taskName);
    napi_create_async_work(env, nullptr, taskName, BackgroundTask, CompletionHandler,
                           ctx, &ctx->asyncTask);
    napi_queue_async_work(env, ctx->asyncTask);
    return promise;
}

Implement the background execution function.

static void BackgroundTask(napi_env env, void *data) {
    TaskContext *ctx = reinterpret_cast<TaskContext *>(data);
    ctx->output = ctx->input * 2;
}

Handle completion by resolving or rejecting the promise.

static void CompletionHandler(napi_env env, napi_status status, void *data) {
    TaskContext *ctx = reinterpret_cast<TaskContext *>(data);
    napi_value result;
    napi_create_double(env, ctx->output, &result);
    if (ctx->output > 0) {
        napi_resolve_deferred(env, ctx->deferredPromise, result);
    } else {
        napi_reject_deferred(env, ctx->deferredPromise, result);
    }
    napi_delete_async_work(env, ctx->asyncTask);
    delete ctx;
}

Initialize the module and call from ArkTS.

static napi_value InitializeModule(napi_env env, napi_value exports) {
    napi_property_descriptor descriptor = {
        "asyncTask", nullptr, StartAsyncTask, nullptr, nullptr, nullptr, napi_default, nullptr
    };
    napi_define_properties(env, exports, 1, &descriptor);
    return exports;
}
nativeModule.asyncTask(512).then((value) => {
    hilog.info(0x0000, 'ModuleTag', 'Result: %{public}d', value);
});

Callback-Based Example

Define a context structure including a callback reference.

struct CallbackContext {
    napi_async_work asyncJob = nullptr;
    napi_ref jsCallback = nullptr;
    double operands[2] = {0};
    double computationResult = 0;
};

Set up the asynchronous work with a callback parameter.

n_value AsyncOperation(napi_env env, napi_callback_info info) {
    size_t argCount = 3;
    napi_value arguments[3];
    napi_get_cb_info(env, info, &argCount, arguments, nullptr, nullptr);
    auto context = new CallbackContext();
    napi_get_value_double(env, arguments[0], &context->operands[0]);
    napi_get_value_double(env, arguments[1], &context->operands[1]);
    napi_create_reference(env, arguments[2], 1, &context->jsCallback);
    napi_value resource;
    napi_create_string_utf8(env, "asyncCallback", NAPI_AUTO_LENGTH, &resource);
    napi_create_async_work(env, nullptr, resource, ExecuteTask, TaskComplete,
                           context, &context->asyncJob);
    napi_queue_async_work(env, context->asyncJob);
    return nullptr;
}

Perform computation in the background.

static void ExecuteTask(napi_env env, void *data) {
    CallbackContext *ctx = reinterpret_cast<CallbackContext *>(data);
    ctx->computationResult = ctx->operands[0] + ctx->operands[1];
}

Invoke the JavaScript callback with the result.

static void TaskComplete(napi_env env, napi_status status, void *data) {
    CallbackContext *ctx = reinterpret_cast<CallbackContext *>(data);
    napi_value callbackArg[1];
    napi_create_double(env, ctx->computationResult, &callbackArg[0]);
    napi_value jsCallback;
    napi_get_reference_value(env, ctx->jsCallback, &jsCallback);
    napi_value undefined;
    napi_get_undefined(env, &undefined);
    napi_value callResult;
    napi_call_function(env, undefined, jsCallback, 1, callbackArg, &callResult);
    napi_delete_reference(env, ctx->jsCallback);
    napi_delete_async_work(env, ctx->asyncJob);
    delete ctx;
}

Module initialization and ArkTS invocation.

static napi_value ModuleInit(napi_env env, napi_value exports) {
    napi_property_descriptor desc = {
        "asyncOperation", nullptr, AsyncOperation, nullptr, nullptr, nullptr, napi_default, nullptr
    };
    napi_define_properties(env, exports, 1, &desc);
    return exports;
}
let valueA: number = 123;
let valueB: number = 456;
nativeModule.asyncOperation(valueA, valueB, (res) => {
    hilog.info(0x0000, 'ModuleTag', 'Result: %{public}d', res);
});
Tags: HarmonyOS

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to Hive SQL Syntax and Operations

This article provides a detailed walkthrough of Hive SQL, categorizing its features and syntax for practical use. Hive SQL is segmented into the following categories: DDL Statements: Operations on...

Understanding the MP4 File Format and Analysis

Table of Contents Overview Fundamentals of the MP4 Format Key Concepts of the Container Format Box Structure Track Samples Sample Tables Chunks Detailed Explanation of Core Boxes Additional Boxes...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.