1# Implementing Asynchronous Operations Using JSVM-API 2 3## Introduction 4 5JSVM-API provides APIs for implementing asynchronous operations. An asynchronous operation is used for a time-consuming task, for example, downloading data from network or reading a large file. Different from a synchronous operation which blocks the main thread, the asynchronous operation is executed in the background. When an asynchronous operation is complete, it will be put into 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 JavaScript (JS). 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 JS, 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**: a JS 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. JSVM-API provides APIs for implementing JS promises in C/C++. 19 20## Available APIs 21 22| API | Description | 23|----------------------------|--------------------------------| 24| OH_JSVM_IsPromise | Checks whether the given **JSVM_Value** is a **Promise** object.| 25| OH_JSVM_CreatePromise | Creates a **deferred** object and a JS promise.| 26| OH_JSVM_ResolveDeferred | Resolves a JS promise by using the **deferred** object associated with it.| 27| OH_JSVM_RejectDeferred | Rejects a JS promise by using the **deferred** object associated with it.| 28| OH_JSVM_PromiseRegisterHandler | Registers a callback to be invoked after a promise is fulfilled or rejected.| 29 30## Example 31 32If you are just starting out with JSVM-API, see [JSVM-API Development Process](use-jsvm-process.md). The following demonstrates only the C++ code involved in promise development. 33 34### OH_JSVM_IsPromise 35 36Call **OH_JSVM_IsPromise** to check whether the given **JSVM_Value** is a **Promise** object. 37 38CPP code: 39 40```cpp 41// hello.cpp 42#include "napi/native_api.h" 43#include "ark_runtime/jsvm.h" 44#include <hilog/log.h> 45// Define OH_JSVM_IsPromise. 46static JSVM_Value IsPromise(JSVM_Env env, JSVM_CallbackInfo info) 47{ 48 size_t argc = 1; 49 JSVM_Value args[1] = {nullptr}; 50 OH_JSVM_GetCbInfo(env, info, &argc, args, nullptr, nullptr); 51 bool isPromise = false; 52 JSVM_Status status = OH_JSVM_IsPromise(env, args[0], &isPromise); 53 if (status != JSVM_OK) { 54 OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_IsPromise fail"); 55 } else { 56 OH_LOG_INFO(LOG_APP, "JSVM OH_JSVM_IsPromise success:%{public}d", isPromise); 57 } 58 JSVM_Value result = nullptr; 59 OH_JSVM_GetBoolean(env, isPromise, &result); 60 return result; 61} 62// Register the IsPromise callback. 63static JSVM_CallbackStruct param[] = { 64 {.data = nullptr, .callback = IsPromise}, 65}; 66static JSVM_CallbackStruct *method = param; 67// Alias for the IsPromise method to be called from JS. 68static JSVM_PropertyDescriptor descriptor[] = { 69 {"isPromise", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 70}; 71 72// Call the C++ code from JS. 73const char *srcCallNative = R"JS(isPromise())JS"; 74``` 75 76Expected result: 77``` 78JSVM OH_JSVM_IsPromise success:0 79``` 80 81### OH_JSVM_CreatePromise 82 83Call **OH_JSVM_CreatePromise** to create a **Promise** object. 84 85### OH_JSVM_ResolveDeferred and OH_JSVM_RejectDeferred 86 87Call **OH_JSVM_ResolveDeferred** to change the promise state from **pending** to **fulfilled**, and call **OH_JSVM_RejectDeferred** to change the promise state from **pending** to **rejected**. 88 89CPP code: 90 91```cpp 92// hello.cpp 93#include "napi/native_api.h" 94#include "ark_runtime/jsvm.h" 95#include <hilog/log.h> 96// Define OH_JSVM_CreatePromise, OH_JSVM_ResolveDeferred, and OH_JSVM_RejectDeferred. 97static JSVM_Value CreatePromise(JSVM_Env env, JSVM_CallbackInfo info) 98{ 99 JSVM_Deferred defer = nullptr; 100 JSVM_Value promise = nullptr; 101 JSVM_Status status = OH_JSVM_CreatePromise(env, &defer, &promise); 102 bool isPromise = false; 103 JSVM_Value returnIsPromise = nullptr; 104 OH_JSVM_IsPromise(env, promise, &isPromise); 105 if (status != JSVM_OK) { 106 OH_LOG_ERROR(LOG_APP, "JSVM CreatePromise fail"); 107 } else { 108 OH_LOG_INFO(LOG_APP, "JSVM CreatePromise success:%{public}d", isPromise); 109 } 110 // Convert the Boolean value to JSVM_Value and return it. 111 OH_JSVM_GetBoolean(env, isPromise, &returnIsPromise); 112 return returnIsPromise; 113} 114 115static JSVM_Value ResolveRejectDeferred(JSVM_Env env, JSVM_CallbackInfo info) 116{ 117 // Obtain and parse parameters. 118 size_t argc = 3; 119 JSVM_Value args[3] = {nullptr}; 120 OH_JSVM_GetCbInfo(env, info, &argc, args, nullptr, nullptr); 121 // 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. 122 bool status = false; 123 OH_JSVM_GetValueBool(env, args[2], &status); 124 // Create a Promise object. 125 JSVM_Deferred deferred = nullptr; 126 JSVM_Value promise = nullptr; 127 JSVM_Status createStatus = OH_JSVM_CreatePromise(env, &deferred, &promise); 128 if (createStatus != JSVM_OK) { 129 OH_JSVM_ThrowError(env, nullptr, "Create promise failed"); 130 return nullptr; 131 } 132 // Set the promise state based on the third parameter. 133 if (status) { 134 OH_JSVM_ResolveDeferred(env, deferred, args[0]); 135 OH_LOG_INFO(LOG_APP, "OH_JSVM_ResolveDeferred resolve"); 136 } else { 137 OH_JSVM_RejectDeferred(env, deferred, args[1]); 138 OH_LOG_INFO(LOG_APP, "OH_JSVM_RejectDeferred reject"); 139 } 140 JSVM_Value result = nullptr; 141 OH_JSVM_GetBoolean(env, true, &result); 142 return result; 143} 144// Register the CreatePromise and ResolveRejectDeferred callbacks. 145static JSVM_CallbackStruct param[] = { 146 {.data = nullptr, .callback = CreatePromise}, 147 {.data = nullptr, .callback = ResolveRejectDeferred}, 148}; 149static JSVM_CallbackStruct *method = param; 150// Aliases for the CreatePromise and ResolveRejectDeferred methods to be called from JS. 151static JSVM_PropertyDescriptor descriptor[] = { 152 {"createPromise", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 153 {"resolveRejectDeferred", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 154}; 155 156// Call the C++ code from JS. 157const char *srcCallNative = R"JS(createPromise(); 158 resolveRejectDeferred('success','fail', true); 159 resolveRejectDeferred('success','fail', false);)JS"; 160``` 161 162Expected result: 163``` 164JSVM CreatePromise success:1 165OH_JSVM_ResolveDeferred resolve 166OH_JSVM_RejectDeferred reject 167``` 168 169## OH_JSVM_PromiseRegisterHandler 170 171Call **OH_JSVM_PromiseRegisterHandler** to register a callback that is invoked after a promise is fulfilled rejected. It is equivalent to calling the native **Promise.then()** or **Promise.catch()**. 172 173The following describes only part of the C++ code. For details about other framework code, such as the **TestJSVM** function, see the implementation of **OH_JSVM_SetMicrotaskPolicy** in [Working with Tasks Using JSVM-API](use-jsvm-execute_tasks.md). 174 175```cpp 176static int PromiseRegisterHandler(JSVM_VM vm, JSVM_Env env) { 177 const char *defineFunction = R"JS( 178 var x1 = 0; 179 var x2 = 0; 180 function f1(x) { 181 x1 = x; 182 return x + 1; 183 } 184 function f2(x) { 185 x2 = x; 186 return x + 1; 187 } 188 )JS"; 189 190 const char *init = R"JS( 191 x1 = 0; 192 x2 = 0; 193 )JS"; 194 195 JSVM_Script script; 196 JSVM_Value jsSrc; 197 JSVM_Value result; 198 199 // Define the JS functions f1 and f2. 200 CHECK_RET(OH_JSVM_CreateStringUtf8(env, defineFunction, JSVM_AUTO_LENGTH, &jsSrc)); 201 CHECK_RET(OH_JSVM_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script)); 202 CHECK_RET(OH_JSVM_RunScript(env, script, &result)); 203 204 // Initialize x1 and x2 to 0. 205 CHECK_RET(OH_JSVM_CreateStringUtf8(env, init, JSVM_AUTO_LENGTH, &jsSrc)); 206 CHECK_RET(OH_JSVM_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script)); 207 CHECK_RET(OH_JSVM_RunScript(env, script, &result)); 208 209 // Obtain functions f1 and f2. 210 JSVM_Value global; 211 CHECK_RET(OH_JSVM_GetGlobal(env, &global)); 212 JSVM_Value f1; 213 CHECK_RET(OH_JSVM_GetNamedProperty(env, global, "f1", &f1)); 214 JSVM_Value f2; 215 CHECK_RET(OH_JSVM_GetNamedProperty(env, global, "f2", &f2)); 216 217 // Create a promise. 218 JSVM_Value promise; 219 JSVM_Deferred deferred; 220 CHECK_RET(OH_JSVM_CreatePromise(env, &deferred, &promise)); 221 // Register a callback for the promise and assign the result (new promise) of then() to promise1. 222 JSVM_Value promise1; 223 CHECK_RET(OH_JSVM_PromiseRegisterHandler(env, promise, f1, nullptr, &promise1)); 224 // Register a callback for promise1. 225 CHECK_RET(OH_JSVM_PromiseRegisterHandler(env, promise1, f2, nullptr, nullptr)); 226 227 // Obtain the values of x1 and x2 before the promise is parsed. 228 JSVM_Value x1; 229 CHECK_RET(OH_JSVM_GetNamedProperty(env, global, "x1", &x1)); 230 int32_t x1Int; 231 CHECK_RET(OH_JSVM_GetValueInt32(env, x1, &x1Int)); 232 JSVM_Value x2; 233 CHECK_RET(OH_JSVM_GetNamedProperty(env, global, "x2", &x2)); 234 int32_t x2Int; 235 CHECK_RET(OH_JSVM_GetValueInt32(env, x2, &x2Int)); 236 OH_LOG_INFO(LOG_APP, "Before promise resolved, x1: %{public}d, x2: %{public}d", x1Int, x2Int); 237 238 // Parse the promise. 239 JSVM_Value resolveValue; 240 CHECK_RET(OH_JSVM_CreateInt32(env, 2, &resolveValue)); 241 OH_JSVM_ResolveDeferred(env, deferred, resolveValue); 242 deferred = nullptr; 243 244 // Obtain the values of x1 and x2 after the promise is parsed. 245 CHECK_RET(OH_JSVM_GetNamedProperty(env, global, "x1", &x1)); 246 CHECK_RET(OH_JSVM_GetValueInt32(env, x1, &x1Int)); 247 CHECK_RET(OH_JSVM_GetNamedProperty(env, global, "x2", &x2)); 248 CHECK_RET(OH_JSVM_GetValueInt32(env, x2, &x2Int)); 249 OH_LOG_INFO(LOG_APP, "After promise resolved, x1: %{public}d, x2: %{public}d", x1Int, x2Int); 250 251 return 0; 252} 253 254static void RunDemo(JSVM_VM vm, JSVM_Env env) { 255 if (PromiseRegisterHandler(vm, env) != 0) { 256 OH_LOG_INFO(LOG_APP, "Run PromiseRegisterHandler failed"); 257 } 258} 259``` 260 261Expected result: 262``` 263Before promise resolved, x1: 0, x2: 0 264After promise resolved, x1: 2, x2: 3 265``` 266