• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 *srcCallNativeCreatePromise = R"JS(createPromise())JS";
158const char *srcCallNativeResolveRejectDeferred1 = R"JS(resolveRejectDeferred('success','fail', true))JS";
159const char *srcCallNativeResolveRejectDeferred2 = R"JS(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
173CPP code:
174```
175static int PromiseRegisterHandler(JSVM_VM vm, JSVM_Env env) {
176    const char *defineFunction = R"JS(
177        var x1 = 0;
178        var x2 = 0;
179        function f1(x) {
180            x1 = x;
181            return x + 1;
182        }
183        function f2(x) {
184            x2 = x;
185            return x + 1;
186        }
187    )JS";
188
189    const char *init = R"JS(
190        x1 = 0;
191        x2 = 0;
192    )JS";
193
194    JSVM_Script script;
195    JSVM_Value jsSrc;
196    JSVM_Value result;
197
198    // Define the JS functions f1 and f2.
199    CHECK_RET(OH_JSVM_CreateStringUtf8(env, defineFunction, JSVM_AUTO_LENGTH, &jsSrc));
200    CHECK_RET(OH_JSVM_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script));
201    CHECK_RET(OH_JSVM_RunScript(env, script, &result));
202
203    // Initialize x1 and x2 to 0.
204    CHECK_RET(OH_JSVM_CreateStringUtf8(env, init, JSVM_AUTO_LENGTH, &jsSrc));
205    CHECK_RET(OH_JSVM_CompileScript(env, jsSrc, nullptr, 0, true, nullptr, &script));
206    CHECK_RET(OH_JSVM_RunScript(env, script, &result));
207
208    // Obtain functions f1 and f2.
209    JSVM_Value global;
210    CHECK_RET(OH_JSVM_GetGlobal(env, &global));
211    JSVM_Value f1;
212    CHECK_RET(OH_JSVM_GetNamedProperty(env, global, "f1", &f1));
213    JSVM_Value f2;
214    CHECK_RET(OH_JSVM_GetNamedProperty(env, global, "f2", &f2));
215
216    // Create a promise.
217    JSVM_Value promise;
218    JSVM_Deferred deferred;
219    CHECK_RET(OH_JSVM_CreatePromise(env, &deferred, &promise));
220    // Register a callback for the promise and assign the result (new promise) of then() to promise1.
221    JSVM_Value promise1;
222    CHECK_RET(OH_JSVM_PromiseRegisterHandler(env, promise, f1, nullptr, &promise1));
223    // Register a callback for promise1.
224    CHECK_RET(OH_JSVM_PromiseRegisterHandler(env, promise1, f2, nullptr, nullptr));
225
226    // Obtain the values of x1 and x2 before the promise is parsed.
227    JSVM_Value x1;
228    CHECK_RET(OH_JSVM_GetNamedProperty(env, global, "x1", &x1));
229    int32_t x1Int;
230    CHECK_RET(OH_JSVM_GetValueInt32(env, x1, &x1Int));
231    JSVM_Value x2;
232    CHECK_RET(OH_JSVM_GetNamedProperty(env, global, "x2", &x2));
233    int32_t x2Int;
234    CHECK_RET(OH_JSVM_GetValueInt32(env, x2, &x2Int));
235    OH_LOG_INFO(LOG_APP, "Before promise resolved, x1: %{public}d, x2: %{public}d", x1Int, x2Int);
236
237    // Parse the promise.
238    JSVM_Value resolveValue;
239    CHECK_RET(OH_JSVM_CreateInt32(env, 2, &resolveValue));
240    OH_JSVM_ResolveDeferred(env, deferred, resolveValue);
241    deferred = nullptr;
242
243    // Obtain the values of x1 and x2 after the promise is parsed.
244    CHECK_RET(OH_JSVM_GetNamedProperty(env, global, "x1", &x1));
245    CHECK_RET(OH_JSVM_GetValueInt32(env, x1, &x1Int));
246    CHECK_RET(OH_JSVM_GetNamedProperty(env, global, "x2", &x2));
247    CHECK_RET(OH_JSVM_GetValueInt32(env, x2, &x2Int));
248    OH_LOG_INFO(LOG_APP, "After promise resolved, x1: %{public}d, x2: %{public}d", x1Int, x2Int);
249
250    return 0;
251}
252
253static void RunDemo(JSVM_VM vm, JSVM_Env env) {
254    if (PromiseRegisterHandler(vm, env) != 0) {
255        OH_LOG_INFO(LOG_APP, "Run PromiseRegisterHandler failed");
256    }
257}
258```
259
260Expected result:
261```
262Before promise resolved, x1: 0, x2: 0
263After promise resolved, x1: 2, x2: 3
264```
265