1# Implementing Asynchronous Operations Using Node-API 2 3## Introduction 4 5Node-API provides APIs for implementing asynchronous operations for time-consuming tasks, such as downloading data from network or reading a large file. Different from synchronous operations, asynchronous operations are executed in the background without blocking the main thread. When an asynchronous operation is complete, it will be added to the task queue and executed when the main thread is idle. 6 7## Basic Concepts 8 9**Promise** is an object used to handle asynchronous operations in ArkTS. It has three states: **pending**, **fulfilled**, and **rejected**. The initial state is **pending**, which can be changed to **fulfilled** by **resolve()** and to **rejected** by **reject()**. Once the state is **fulfilled** or **rejected**, the promise state cannot be changed. Read on the following to learn basic concepts related to **Promise**: 10 11- Synchronous: Code is executed line by line in sequence. Each line of code is executed after the previous line of code is executed. During synchronous execution, if an operation takes a long time, the execution of the entire application will be blocked until the operation is complete. 12- Asynchronous: Tasks can be executed concurrently without waiting for the end of the previous task. In ArkTS, common asynchronous operations apply for timers, event listening, and network requests. Instead of blocking subsequent tasks, the asynchronous task uses a callback or promise to process its result. 13- **Promise**: an ArkTS object used to handle asynchronous operations. Generally, it is exposed externally by using **then()**, **catch()**, or **finally()** to custom logic. 14- **deferred**: a utility object associated with the **Promise** object to set **resolve()** and **reject()** of **Promise**. It is used internally to maintain the state of the asynchronous model and set the **resolve()** and **reject()** callbacks. 15- **resolve**: a function used to change the promise state from **pending** to **fulfilled**. The parameters passed to **resolve()** can be obtained from **then()** of the **Promise** object. 16- **reject**: a function used to change the promise state from **pending** to **rejected**. The parameters passed to **reject()** can be obtained from **catch()** of the **Promise** object. 17 18**Promise** allows multiple callbacks to be called in a chain, providing better code readability and a better way to deal with asynchronous operations. The APIs provided by the Node-API module help you flexibly process ArkTS asynchronous operations in C/C++. 19 20## Available APIs 21 22The following table lists the APIs for implementing asynchronous operations using ArkTS promises. 23 24| API| Description| 25| -------- | -------- | 26| napi_is_promise | Checks whether a **napi_value** is a **Promise** object.| 27| napi_create_promise | Creates a **Promise** object.| 28| napi_resolve_deferred | Resolves a promise by using the **deferred** object associated with it.| 29| napi_reject_deferred | Rejects a promise by using the **deferred** object associated with it| 30 31## Example 32 33If you are just starting out with Node-API, see [Node-API Development Process](use-napi-process.md). The following demonstrates only the C++ and ArkTS code related to promise development. 34 35### napi_is_promise 36 37Call **napi_is_promise** to check whether the given **napi_value** is a **Promise** object. 38 39CPP code: 40 41```cpp 42#include "napi/native_api.h" 43 44static napi_value IsPromise(napi_env env, napi_callback_info info) 45{ 46 napi_value argv[1] = {nullptr}; 47 size_t argc = 1; 48 napi_status status; 49 // Obtain the parameters passed in. 50 napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); 51 bool isPromise = false; 52 // Check whether the given parameter is a Promise object and save the result in the isPromise variable. 53 status = napi_is_promise(env, argv[0], &isPromise); 54 if (status != napi_ok) { 55 napi_throw_error(env, nullptr, "Node-API napi_is_promise failed"); 56 return nullptr; 57 } 58 napi_value result = nullptr; 59 // Convert the value of isPromise to the type specified by napi_value, and return it. 60 napi_get_boolean(env, isPromise, &result); 61 return result; 62} 63``` 64 65API declaration: 66 67```ts 68// index.d.ts 69export const isPromise: <T>(value: T) => boolean; 70``` 71 72ArkTS code: 73 74```ts 75import hilog from '@ohos.hilog'; 76import testNapi from 'libentry.so'; 77 78let value = Promise.resolve(); 79// Return true if the object passed in is a promise; return false otherwise. 80hilog.info(0x0000, 'Node-API', 'napi_is_promise %{public}s', testNapi.isPromise(value)); 81hilog.info(0x0000, 'Node-API', 'napi_is_promise string %{public}s', testNapi.isPromise('')); 82``` 83 84### napi_create_promise 85 86Call **napi_create_promise** to create a **Promise** object. 87 88When using this API, observe to the following: 89 90- If **napi_create_promise** is called when there is an exception not handled, **napi_pending_exception** will be returned. 91- After calling **napi_create_promise**, always check whether the return value is **napi_ok**. If **deferred** and **promise** are used, the application will crash. 92 93```c++ 94napi_value NapiPromiseDemo(napi_env env, napi_callback_info) 95{ 96 napi_deferred deferred = nullptr; 97 napi_value promise = nullptr; 98 napi_status status = napi_ok; 99 100 napi_throw_error(env, "500", "common error"); 101 102 status = napi_create_promise(env, &deferred, &promise); // If there is an error, return napi_pending_exception with deferred and promise set to nullptr. 103 if (status == napi_ok) { 104 // do something 105 } 106 107 return nullptr; 108} 109``` 110 111### napi_resolve_deferred & napi_reject_deferred 112 113Call **napi_resolve_deferred** to change the promise state from **pending** to **fulfilled**, and call **napi_reject_deferred** to change the promise state from **pending** to **rejected**. 114 115To ensure execution of microtasks, the ArkTS runtime will trigger a microtask execution when fulfilling a promise using Node-API. 116 117CPP code: 118 119```cpp 120#include "napi/native_api.h" 121 122static constexpr int INT_ARG_2 = 2; // Input parameter index. 123 124static napi_value CreatePromise(napi_env env, napi_callback_info info) 125{ 126 // The deferred object is used to delay the execution of a function for a certain period of time. 127 napi_deferred deferred = nullptr; 128 napi_value promise = nullptr; 129 // Create a Promise object. 130 napi_status status = napi_create_promise(env, &deferred, &promise); 131 if (status != napi_ok) { 132 napi_throw_error(env, nullptr, "Create promise failed"); 133 return nullptr; 134 } 135 //Call napi_is_promise to check whether the object created by napi_create_promise is a Promise object. 136 bool isPromise = false; 137 napi_value returnIsPromise = nullptr; 138 napi_is_promise(env, promise, &isPromise); 139 // Convert the Boolean value to napi_value and return it. 140 napi_get_boolean(env, isPromise, &returnIsPromise); 141 return returnIsPromise; 142} 143 144static napi_value ResolveRejectDeferred(napi_env env, napi_callback_info info) 145{ 146 // Obtain and parse parameters. 147 size_t argc = 3; 148 napi_value args[3] = {nullptr}; 149 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 150 // The first parameter is the data to be passed to Resolve(), the second parameter is the data to be passed to reject(), and the third parameter is the Promise state. 151 bool status; 152 napi_get_value_bool(env, args[INT_ARG_2], &status); 153 // Create a Promise object. 154 napi_deferred deferred = nullptr; 155 napi_value promise = nullptr; 156 napi_status createStatus = napi_create_promise(env, &deferred, &promise); 157 if (createStatus != napi_ok) { 158 napi_throw_error(env, nullptr, "Create promise failed"); 159 return nullptr; 160 } 161 // Set the promise state based on the third parameter. 162 if (status) { 163 napi_resolve_deferred(env, deferred, args[0]); 164 } else { 165 napi_reject_deferred(env, deferred, args[1]); 166 } 167 // Return the Promise object with the state set. 168 return promise; 169} 170``` 171 172API declaration: 173 174```ts 175// index.d.ts 176export const createPromise: () => boolean | void; 177export const resolveRejectDeferred: (resolve: string, reject: string, status: boolean) => Promise<string> | void; 178``` 179 180ArkTS code: 181 182```ts 183import hilog from '@ohos.hilog'; 184import testNapi from 'libentry.so'; 185 186// Create a promise. Return true if the operation is successful, and return false otherwise. 187hilog.info(0x0000, 'Node-API', 'napi_create_promise %{public}s', testNapi.createPromise()); 188// Call resolveRejectDeferred to resolve or reject the promise and set the promise state. 189// Resolve the promise. The return value is passed to the then function. 190let promiseSuccess: Promise<string> = testNapi.resolveRejectDeferred('success', 'fail', true) as Promise<string>; 191promiseSuccess.then((res) => { 192 hilog.info(0x0000, 'Node-API', 'get_resolve_deferred resolve %{public}s', res) 193}).catch((err: Error) => { 194 hilog.info(0x0000, 'Node-API', 'get_resolve_deferred reject %{public}s', err) 195}) 196// Reject the promise. The return value is passed to the catch function. 197let promiseFail: Promise<string> = testNapi.resolveRejectDeferred('success', 'fail', false) as Promise<string>; 198promiseFail.then((res) => { 199 hilog.info(0x0000, 'Node-API', 'get_resolve_deferred resolve %{public}s', res) 200}).catch((err: Error) => { 201 hilog.info(0x0000, 'Node-API', 'get_resolve_deferred reject %{public}s', err) 202}) 203``` 204 205To 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"**. 206 207```text 208// CMakeLists.txt 209add_definitions( "-DLOG_DOMAIN=0xd0d0" ) 210add_definitions( "-DLOG_TAG=\"testTag\"" ) 211target_link_libraries(entry PUBLIC libhilog_ndk.z.so) 212``` 213