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
29## Example
30
31If you are just starting out with JSVM-API, see [JSVM-API Development Process](use-jsvm-process.md). The following only demonstrates the C++ and ArkTS code related to promises.
32
33### OH_JSVM_IsPromise
34
35Use **OH_JSVM_IsPromise** to check whether the given **JSVM_Value** is a **Promise** object.
36
37CPP code:
38
39```cpp
40// hello.cpp
41#include "napi/native_api.h"
42#include "ark_runtime/jsvm.h"
43#include <hilog/log.h>
44// Register the IsPromise callback.
45static JSVM_CallbackStruct param[] = {
46    {.data = nullptr, .callback = IsPromise},
47};
48static JSVM_CallbackStruct *method = param;
49// Set a property descriptor named isPromise and associate it with a callback. This allows the IsPromise callback to be called from JS.
50static JSVM_PropertyDescriptor descriptor[] = {
51    {"isPromise", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT},
52};
53// Define OH_JSVM_IsPromise.
54static JSVM_Value IsPromise(JSVM_Env env, JSVM_CallbackInfo info)
55{
56    size_t argc = 1;
57    JSVM_Value args[1] = {nullptr};
58    OH_JSVM_GetCbInfo(env, info, &argc, args, nullptr, nullptr);
59    bool isPromise = false;
60    JSVM_Status status = OH_JSVM_IsPromise(env, args[0], &isPromise);
61    if (status != JSVM_OK) {
62        OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_IsPromise fail");
63    } else {
64        OH_LOG_INFO(LOG_APP, "JSVM OH_JSVM_IsPromise success:%{public}d", isPromise);
65    }
66    JSVM_Value result = nullptr;
67    OH_JSVM_GetBoolean(env, isPromise, &result);
68    return result;
69}
70```
71
72ArkTS code:
73
74```ts
75import hilog from "@ohos.hilog"
76// Import the native APIs.
77import napitest from "libentry.so"
78let script: string = `
79          let value = Promise.resolve();
80          isPromise(value);
81        `;
82try {
83  let result = napitest.runJsVm(script);
84  hilog.info(0x0000, 'JSVM', 'IsPromise: %{public}s', result);
85} catch (error) {
86  hilog.error(0x0000, 'JSVM', 'IsPromise: %{public}s', error.message);
87}
88```
89
90### OH_JSVM_CreatePromise
91
92Use **OH_JSVM_CreatePromise** to create a **Promise** object.
93
94### OH_JSVM_ResolveDeferred & OH_JSVM_RejectDeferred
95
96Use **OH_JSVM_ResolveDeferred** to change the promise state from **pending** to **fulfilled**, and use **OH_JSVM_RejectDeferred** to change the promise state from **pending** to **rejected**.
97
98CPP code:
99
100```cpp
101// hello.cpp
102#include "napi/native_api.h"
103#include "ark_runtime/jsvm.h"
104#include <hilog/log.h>
105// Register the CreatePromise and ResolveRejectDeferred callbacks.
106static JSVM_CallbackStruct param[] = {
107    {.data = nullptr, .callback = CreatePromise},
108    {.data = nullptr, .callback = ResolveRejectDeferred},
109};
110static JSVM_CallbackStruct *method = param;
111// Set property descriptor named createPromise and resolveRejectDeferred and associate them with a callback each. This allows the CreatePromise and ResolveRejectDeferred callbacks to be called from JS.
112static JSVM_PropertyDescriptor descriptor[] = {
113    {"createPromise", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT},
114    {"resolveRejectDeferred", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT},
115};
116// Define OH_JSVM_CreatePromise, OH_JSVM_ResolveDeferred, and OH_JSVM_RejectDeferred.
117static JSVM_Value CreatePromise(JSVM_Env env, JSVM_CallbackInfo info)
118{
119    JSVM_Deferred defer = nullptr;
120    JSVM_Value promise = nullptr;
121    JSVM_Status status = OH_JSVM_CreatePromise(env, &defer, &promise);
122    bool isPromise = false;
123    JSVM_Value returnIsPromise = nullptr;
124    OH_JSVM_IsPromise(env, promise, &isPromise);
125    if (status != JSVM_OK) {
126        OH_LOG_ERROR(LOG_APP, "JSVM CreatePromise fail");
127    } else {
128        OH_LOG_INFO(LOG_APP, "JSVM CreatePromise success:%{public}d", isPromise);
129    }
130    // Convert the Boolean value to JSVM_Value and return it.
131    OH_JSVM_GetBoolean(env, isPromise, &returnIsPromise);
132    return returnIsPromise;
133}
134
135static JSVM_Value ResolveRejectDeferred(JSVM_Env env, JSVM_CallbackInfo info)
136{
137    // Obtain and parse parameters.
138    size_t argc = 3;
139    JSVM_Value args[3] = {nullptr};
140    OH_JSVM_GetCbInfo(env, info, &argc, args, nullptr, nullptr);
141    // 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.
142    bool status = false;
143    OH_JSVM_GetValueBool(env, args[2], &status);
144    // Create a Promise object.
145    JSVM_Deferred deferred = nullptr;
146    JSVM_Value promise = nullptr;
147    JSVM_Status createStatus = OH_JSVM_CreatePromise(env, &deferred, &promise);
148    if (createStatus != JSVM_OK) {
149        OH_JSVM_ThrowError(env, nullptr, "Create promise failed");
150        return nullptr;
151    }
152    // Set the promise state based on the third parameter.
153    if (status) {
154        OH_JSVM_ResolveDeferred(env, deferred, args[0]);
155        OH_LOG_INFO(LOG_APP, "OH_JSVM_ResolveDeferred resolve");
156    } else {
157        OH_JSVM_RejectDeferred(env, deferred, args[1]);
158        OH_LOG_INFO(LOG_APP, "OH_JSVM_RejectDeferred reject");
159    }
160    JSVM_Value result = nullptr;
161    OH_JSVM_GetBoolean(env, true, &result);
162    return result;
163}
164```
165
166ArkTS code:
167
168```ts
169import hilog from "@ohos.hilog"
170// Import the native APIs.
171import napitest from "libentry.so"
172let createPromiseScript: string = `createPromise();`;
173let createPromiseresult = napitest.runJsVm(createPromiseScript);
174hilog.info(0x0000, 'JSVM', 'CreatePromise: %{public}s', createPromiseresult);
175// The third input parameter means to change the Promise state from pending to fulfilled.
176let resolveScript: string = `resolveRejectDeferred('success','fail', true);`;
177let result = napitest.runJsVm(resolveScript);
178hilog.info(0x0000, 'JSVM', 'ResolveRejectDeferred: %{public}s', result);
179// The third input parameter means to change the Promise state from pending to rejected.
180let rejectScript: string = `resolveRejectDeferred('success','fail', false);`;
181let rejectResult = napitest.runJsVm(rejectScript);
182hilog.info(0x0000, 'JSVM', 'ResolveRejectDeferred: %{public}s', rejectResult);
183```
184