n-api: implement promise
Promise is implemented as a pair of objects. `napi_create_promise()` returns both a JavaScript promise and a newly allocated "deferred" in its out-params. The deferred is linked to the promise such that the deferred can be passed to `napi_resolve_deferred()` or `napi_reject_deferred()` to reject/resolve the promise. `napi_is_promise()` can be used to check if a `napi_value` is a native promise - that is, a promise created by the underlying engine, rather than a pure JS implementation of a promise. PR-URL: https://github.com/nodejs/node/pull/14365 Fixes: https://github.com/nodejs/abi-stable-node/issues/242 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Timothy Gu <timothygu99@gmail.com>
This commit is contained in:
parent
64f59be62f
commit
7efb8f7619
138
doc/api/n-api.md
138
doc/api/n-api.md
@ -42,6 +42,7 @@ The documentation for N-API is structured as follows:
|
|||||||
* [Working with JavaScript Functions][]
|
* [Working with JavaScript Functions][]
|
||||||
* [Object Wrap][]
|
* [Object Wrap][]
|
||||||
* [Asynchronous Operations][]
|
* [Asynchronous Operations][]
|
||||||
|
* [Promises][]
|
||||||
|
|
||||||
The N-API is a C API that ensures ABI stability across Node.js versions
|
The N-API is a C API that ensures ABI stability across Node.js versions
|
||||||
and different compiler levels. However, we also understand that a C++
|
and different compiler levels. However, we also understand that a C++
|
||||||
@ -3395,6 +3396,142 @@ support it:
|
|||||||
|
|
||||||
<!-- it's very convenient to have all the anchors indexed -->
|
<!-- it's very convenient to have all the anchors indexed -->
|
||||||
<!--lint disable no-unused-definitions remark-lint-->
|
<!--lint disable no-unused-definitions remark-lint-->
|
||||||
|
## Promises
|
||||||
|
|
||||||
|
N-API provides facilities for creating `Promise` objects as described in
|
||||||
|
[Section 25.4][] of the ECMA specification. It implements promises as a pair of
|
||||||
|
objects. When a promise is created by `napi_create_promise()`, a "deferred"
|
||||||
|
object is created and returned alongside the `Promise`. The deferred object is
|
||||||
|
bound to the created `Promise` and is the only means to resolve or reject the
|
||||||
|
`Promise` using `napi_resolve_deferred()` or `napi_reject_deferred()`. The
|
||||||
|
deferred object that is created by `napi_create_promise()` is freed by
|
||||||
|
`napi_resolve_deferred()` or `napi_reject_deferred()`. The `Promise` object may
|
||||||
|
be returned to JavaScript where it can be used in the usual fashion.
|
||||||
|
|
||||||
|
For example, to create a promise and pass it to an asynchronous worker:
|
||||||
|
```c
|
||||||
|
napi_deferred deferred;
|
||||||
|
napi_value promise;
|
||||||
|
napi_status status;
|
||||||
|
|
||||||
|
// Create the promise.
|
||||||
|
status = napi_create_promise(env, &deferred, &promise);
|
||||||
|
if (status != napi_ok) return NULL;
|
||||||
|
|
||||||
|
// Pass the deferred to a function that performs an asynchronous action.
|
||||||
|
do_something_asynchronous(deferred);
|
||||||
|
|
||||||
|
// Return the promise to JS
|
||||||
|
return promise;
|
||||||
|
```
|
||||||
|
|
||||||
|
The above function `do_something_asynchronous()` would perform its asynchronous
|
||||||
|
action and then it would resolve or reject the deferred, thereby concluding the
|
||||||
|
promise and freeing the deferred:
|
||||||
|
```c
|
||||||
|
napi_deferred deferred;
|
||||||
|
napi_value undefined;
|
||||||
|
napi_status status;
|
||||||
|
|
||||||
|
// Create a value with which to conclude the deferred.
|
||||||
|
status = napi_get_undefined(env, &undefined);
|
||||||
|
if (status != napi_ok) return NULL;
|
||||||
|
|
||||||
|
// Resolve or reject the promise associated with the deferred depending on
|
||||||
|
// whether the asynchronous action succeeded.
|
||||||
|
if (asynchronous_action_succeeded) {
|
||||||
|
status = napi_resolve_deferred(env, deferred, undefined);
|
||||||
|
} else {
|
||||||
|
status = napi_reject_deferred(env, deferred, undefined);
|
||||||
|
}
|
||||||
|
if (status != napi_ok) return NULL;
|
||||||
|
|
||||||
|
// At this point the deferred has been freed, so we should assign NULL to it.
|
||||||
|
deferred = NULL;
|
||||||
|
```
|
||||||
|
|
||||||
|
### napi_create_promise
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
```C
|
||||||
|
NAPI_EXTERN napi_status napi_create_promise(napi_env env,
|
||||||
|
napi_deferred* deferred,
|
||||||
|
napi_value* promise);
|
||||||
|
```
|
||||||
|
|
||||||
|
- `[in] env`: The environment that the API is invoked under.
|
||||||
|
- `[out] deferred`: A newly created deferred object which can later be passed to
|
||||||
|
`napi_resolve_deferred()` or `napi_reject_deferred()` to resolve resp. reject
|
||||||
|
the associated promise.
|
||||||
|
- `[out] promise`: The JavaScript promise associated with the deferred object.
|
||||||
|
|
||||||
|
Returns `napi_ok` if the API succeeded.
|
||||||
|
|
||||||
|
This API creates a deferred object and a JavaScript promise.
|
||||||
|
|
||||||
|
### napi_resolve_deferred
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
```C
|
||||||
|
NAPI_EXTERN napi_status napi_resolve_deferred(napi_env env,
|
||||||
|
napi_deferred deferred,
|
||||||
|
napi_value resolution);
|
||||||
|
```
|
||||||
|
|
||||||
|
- `[in] env`: The environment that the API is invoked under.
|
||||||
|
- `[in] deferred`: The deferred object whose associated promise to resolve.
|
||||||
|
- `[in] resolution`: The value with which to resolve the promise.
|
||||||
|
|
||||||
|
This API resolves a JavaScript promise by way of the deferred object
|
||||||
|
with which it is associated. Thus, it can only be used to resolve JavaScript
|
||||||
|
promises for which the corresponding deferred object is available. This
|
||||||
|
effectively means that the promise must have been created using
|
||||||
|
`napi_create_promise()` and the deferred object returned from that call must
|
||||||
|
have been retained in order to be passed to this API.
|
||||||
|
|
||||||
|
The deferred object is freed upon successful completion.
|
||||||
|
|
||||||
|
### napi_reject_deferred
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
```C
|
||||||
|
NAPI_EXTERN napi_status napi_reject_deferred(napi_env env,
|
||||||
|
napi_deferred deferred,
|
||||||
|
napi_value rejection);
|
||||||
|
```
|
||||||
|
|
||||||
|
- `[in] env`: The environment that the API is invoked under.
|
||||||
|
- `[in] deferred`: The deferred object whose associated promise to resolve.
|
||||||
|
- `[in] rejection`: The value with which to reject the promise.
|
||||||
|
|
||||||
|
This API rejects a JavaScript promise by way of the deferred object
|
||||||
|
with which it is associated. Thus, it can only be used to reject JavaScript
|
||||||
|
promises for which the corresponding deferred object is available. This
|
||||||
|
effectively means that the promise must have been created using
|
||||||
|
`napi_create_promise()` and the deferred object returned from that call must
|
||||||
|
have been retained in order to be passed to this API.
|
||||||
|
|
||||||
|
The deferred object is freed upon successful completion.
|
||||||
|
|
||||||
|
### napi_is_promise
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
```C
|
||||||
|
NAPI_EXTERN napi_status napi_is_promise(napi_env env,
|
||||||
|
napi_value promise,
|
||||||
|
bool* is_promise);
|
||||||
|
```
|
||||||
|
|
||||||
|
- `[in] env`: The environment that the API is invoked under.
|
||||||
|
- `[in] promise`: The promise to examine
|
||||||
|
- `[out] is_promise`: Flag indicating whether `promise` is a native promise
|
||||||
|
object - that is, a promise object created by the underlying engine.
|
||||||
|
|
||||||
|
[Promises]: #n_api_promises
|
||||||
[Asynchronous Operations]: #n_api_asynchronous_operations
|
[Asynchronous Operations]: #n_api_asynchronous_operations
|
||||||
[Basic N-API Data Types]: #n_api_basic_n_api_data_types
|
[Basic N-API Data Types]: #n_api_basic_n_api_data_types
|
||||||
[ECMAScript Language Specification]: https://tc39.github.io/ecma262/
|
[ECMAScript Language Specification]: https://tc39.github.io/ecma262/
|
||||||
@ -3406,6 +3543,7 @@ support it:
|
|||||||
[Section 9.1.6]: https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc
|
[Section 9.1.6]: https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc
|
||||||
[Section 12.5.5]: https://tc39.github.io/ecma262/#sec-typeof-operator
|
[Section 12.5.5]: https://tc39.github.io/ecma262/#sec-typeof-operator
|
||||||
[Section 24.3]: https://tc39.github.io/ecma262/#sec-dataview-objects
|
[Section 24.3]: https://tc39.github.io/ecma262/#sec-dataview-objects
|
||||||
|
[Section 25.4]: https://tc39.github.io/ecma262/#sec-promise-objects
|
||||||
[Working with JavaScript Functions]: #n_api_working_with_javascript_functions
|
[Working with JavaScript Functions]: #n_api_working_with_javascript_functions
|
||||||
[Working with JavaScript Properties]: #n_api_working_with_javascript_properties
|
[Working with JavaScript Properties]: #n_api_working_with_javascript_properties
|
||||||
[Working with JavaScript Values]: #n_api_working_with_javascript_values
|
[Working with JavaScript Values]: #n_api_working_with_javascript_values
|
||||||
|
@ -218,6 +218,14 @@ V8EscapableHandleScopeFromJsEscapableHandleScope(
|
|||||||
static_assert(sizeof(v8::Local<v8::Value>) == sizeof(napi_value),
|
static_assert(sizeof(v8::Local<v8::Value>) == sizeof(napi_value),
|
||||||
"Cannot convert between v8::Local<v8::Value> and napi_value");
|
"Cannot convert between v8::Local<v8::Value> and napi_value");
|
||||||
|
|
||||||
|
napi_deferred JsDeferredFromV8Persistent(v8::Persistent<v8::Value>* local) {
|
||||||
|
return reinterpret_cast<napi_deferred>(local);
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::Persistent<v8::Value>* V8PersistentFromJsDeferred(napi_deferred local) {
|
||||||
|
return reinterpret_cast<v8::Persistent<v8::Value>*>(local);
|
||||||
|
}
|
||||||
|
|
||||||
napi_value JsValueFromV8LocalValue(v8::Local<v8::Value> local) {
|
napi_value JsValueFromV8LocalValue(v8::Local<v8::Value> local) {
|
||||||
return reinterpret_cast<napi_value>(*local);
|
return reinterpret_cast<napi_value>(*local);
|
||||||
}
|
}
|
||||||
@ -774,6 +782,33 @@ napi_status Unwrap(napi_env env,
|
|||||||
return napi_ok;
|
return napi_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
napi_status ConcludeDeferred(napi_env env,
|
||||||
|
napi_deferred deferred,
|
||||||
|
napi_value result,
|
||||||
|
bool is_resolved) {
|
||||||
|
NAPI_PREAMBLE(env);
|
||||||
|
CHECK_ARG(env, result);
|
||||||
|
|
||||||
|
v8::Local<v8::Context> context = env->isolate->GetCurrentContext();
|
||||||
|
v8::Persistent<v8::Value>* deferred_ref =
|
||||||
|
V8PersistentFromJsDeferred(deferred);
|
||||||
|
v8::Local<v8::Value> v8_deferred =
|
||||||
|
v8::Local<v8::Value>::New(env->isolate, *deferred_ref);
|
||||||
|
|
||||||
|
auto v8_resolver = v8::Local<v8::Promise::Resolver>::Cast(v8_deferred);
|
||||||
|
|
||||||
|
v8::Maybe<bool> success = is_resolved ?
|
||||||
|
v8_resolver->Resolve(context, v8impl::V8LocalValueFromJsValue(result)) :
|
||||||
|
v8_resolver->Reject(context, v8impl::V8LocalValueFromJsValue(result));
|
||||||
|
|
||||||
|
deferred_ref->Reset();
|
||||||
|
delete deferred_ref;
|
||||||
|
|
||||||
|
RETURN_STATUS_IF_FALSE(env, success.FromMaybe(false), napi_generic_failure);
|
||||||
|
|
||||||
|
return GET_RETURN_STATUS(env);
|
||||||
|
}
|
||||||
|
|
||||||
} // end of namespace v8impl
|
} // end of namespace v8impl
|
||||||
|
|
||||||
// Intercepts the Node-V8 module registration callback. Converts parameters
|
// Intercepts the Node-V8 module registration callback. Converts parameters
|
||||||
@ -3332,3 +3367,46 @@ napi_status napi_cancel_async_work(napi_env env, napi_async_work work) {
|
|||||||
|
|
||||||
return napi_clear_last_error(env);
|
return napi_clear_last_error(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NAPI_EXTERN napi_status napi_create_promise(napi_env env,
|
||||||
|
napi_deferred* deferred,
|
||||||
|
napi_value* promise) {
|
||||||
|
NAPI_PREAMBLE(env);
|
||||||
|
CHECK_ARG(env, deferred);
|
||||||
|
CHECK_ARG(env, promise);
|
||||||
|
|
||||||
|
auto maybe = v8::Promise::Resolver::New(env->isolate->GetCurrentContext());
|
||||||
|
CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
|
||||||
|
|
||||||
|
auto v8_resolver = maybe.ToLocalChecked();
|
||||||
|
auto v8_deferred = new v8::Persistent<v8::Value>();
|
||||||
|
v8_deferred->Reset(env->isolate, v8_resolver);
|
||||||
|
|
||||||
|
*deferred = v8impl::JsDeferredFromV8Persistent(v8_deferred);
|
||||||
|
*promise = v8impl::JsValueFromV8LocalValue(v8_resolver->GetPromise());
|
||||||
|
return GET_RETURN_STATUS(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
NAPI_EXTERN napi_status napi_resolve_deferred(napi_env env,
|
||||||
|
napi_deferred deferred,
|
||||||
|
napi_value resolution) {
|
||||||
|
return v8impl::ConcludeDeferred(env, deferred, resolution, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
NAPI_EXTERN napi_status napi_reject_deferred(napi_env env,
|
||||||
|
napi_deferred deferred,
|
||||||
|
napi_value resolution) {
|
||||||
|
return v8impl::ConcludeDeferred(env, deferred, resolution, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
NAPI_EXTERN napi_status napi_is_promise(napi_env env,
|
||||||
|
napi_value promise,
|
||||||
|
bool* is_promise) {
|
||||||
|
CHECK_ENV(env);
|
||||||
|
CHECK_ARG(env, promise);
|
||||||
|
CHECK_ARG(env, is_promise);
|
||||||
|
|
||||||
|
*is_promise = v8impl::V8LocalValueFromJsValue(promise)->IsPromise();
|
||||||
|
|
||||||
|
return napi_clear_last_error(env);
|
||||||
|
}
|
||||||
|
@ -543,6 +543,20 @@ NAPI_EXTERN
|
|||||||
napi_status napi_get_node_version(napi_env env,
|
napi_status napi_get_node_version(napi_env env,
|
||||||
const napi_node_version** version);
|
const napi_node_version** version);
|
||||||
|
|
||||||
|
// Promises
|
||||||
|
NAPI_EXTERN napi_status napi_create_promise(napi_env env,
|
||||||
|
napi_deferred* deferred,
|
||||||
|
napi_value* promise);
|
||||||
|
NAPI_EXTERN napi_status napi_resolve_deferred(napi_env env,
|
||||||
|
napi_deferred deferred,
|
||||||
|
napi_value resolution);
|
||||||
|
NAPI_EXTERN napi_status napi_reject_deferred(napi_env env,
|
||||||
|
napi_deferred deferred,
|
||||||
|
napi_value rejection);
|
||||||
|
NAPI_EXTERN napi_status napi_is_promise(napi_env env,
|
||||||
|
napi_value promise,
|
||||||
|
bool* is_promise);
|
||||||
|
|
||||||
EXTERN_C_END
|
EXTERN_C_END
|
||||||
|
|
||||||
#endif // SRC_NODE_API_H_
|
#endif // SRC_NODE_API_H_
|
||||||
|
@ -17,6 +17,7 @@ typedef struct napi_handle_scope__ *napi_handle_scope;
|
|||||||
typedef struct napi_escapable_handle_scope__ *napi_escapable_handle_scope;
|
typedef struct napi_escapable_handle_scope__ *napi_escapable_handle_scope;
|
||||||
typedef struct napi_callback_info__ *napi_callback_info;
|
typedef struct napi_callback_info__ *napi_callback_info;
|
||||||
typedef struct napi_async_work__ *napi_async_work;
|
typedef struct napi_async_work__ *napi_async_work;
|
||||||
|
typedef struct napi_deferred__ *napi_deferred;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
napi_default = 0,
|
napi_default = 0,
|
||||||
|
8
test/addons-napi/test_promise/binding.gyp
Normal file
8
test/addons-napi/test_promise/binding.gyp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"target_name": "test_promise",
|
||||||
|
"sources": [ "test_promise.c" ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
60
test/addons-napi/test_promise/test.js
Normal file
60
test/addons-napi/test_promise/test.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../../common');
|
||||||
|
const test_promise = require(`./build/${common.buildType}/test_promise`);
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
let expected_result, promise;
|
||||||
|
|
||||||
|
// A resolution
|
||||||
|
expected_result = 42;
|
||||||
|
promise = test_promise.createPromise();
|
||||||
|
promise.then(
|
||||||
|
common.mustCall(function(result) {
|
||||||
|
assert.strictEqual(result, expected_result,
|
||||||
|
'promise resolved as expected');
|
||||||
|
}),
|
||||||
|
common.mustNotCall());
|
||||||
|
test_promise.concludeCurrentPromise(expected_result, true);
|
||||||
|
|
||||||
|
// A rejection
|
||||||
|
expected_result = 'It\'s not you, it\'s me.';
|
||||||
|
promise = test_promise.createPromise();
|
||||||
|
promise.then(
|
||||||
|
common.mustNotCall(),
|
||||||
|
common.mustCall(function(result) {
|
||||||
|
assert.strictEqual(result, expected_result,
|
||||||
|
'promise rejected as expected');
|
||||||
|
}));
|
||||||
|
test_promise.concludeCurrentPromise(expected_result, false);
|
||||||
|
|
||||||
|
// Chaining
|
||||||
|
promise = test_promise.createPromise();
|
||||||
|
promise.then(
|
||||||
|
common.mustCall(function(result) {
|
||||||
|
assert.strictEqual(result, 'chained answer',
|
||||||
|
'resolving with a promise chains properly');
|
||||||
|
}),
|
||||||
|
common.mustNotCall());
|
||||||
|
test_promise.concludeCurrentPromise(Promise.resolve('chained answer'), true);
|
||||||
|
|
||||||
|
assert.strictEqual(test_promise.isPromise(promise), true,
|
||||||
|
'natively created promise is recognized as a promise');
|
||||||
|
|
||||||
|
assert.strictEqual(test_promise.isPromise(Promise.reject(-1)), true,
|
||||||
|
'Promise created with JS is recognized as a promise');
|
||||||
|
|
||||||
|
assert.strictEqual(test_promise.isPromise(2.4), false,
|
||||||
|
'Number is recognized as not a promise');
|
||||||
|
|
||||||
|
assert.strictEqual(test_promise.isPromise('I promise!'), false,
|
||||||
|
'String is recognized as not a promise');
|
||||||
|
|
||||||
|
assert.strictEqual(test_promise.isPromise(undefined), false,
|
||||||
|
'undefined is recognized as not a promise');
|
||||||
|
|
||||||
|
assert.strictEqual(test_promise.isPromise(null), false,
|
||||||
|
'null is recognized as not a promise');
|
||||||
|
|
||||||
|
assert.strictEqual(test_promise.isPromise({}), false,
|
||||||
|
'an object is recognized as not a promise');
|
60
test/addons-napi/test_promise/test_promise.c
Normal file
60
test/addons-napi/test_promise/test_promise.c
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#include <node_api.h>
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
napi_deferred deferred = NULL;
|
||||||
|
|
||||||
|
napi_value createPromise(napi_env env, napi_callback_info info) {
|
||||||
|
napi_value promise;
|
||||||
|
|
||||||
|
// We do not overwrite an existing deferred.
|
||||||
|
if (deferred != NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
napi_value concludeCurrentPromise(napi_env env, napi_callback_info info) {
|
||||||
|
napi_value argv[2];
|
||||||
|
size_t argc = 2;
|
||||||
|
bool resolution;
|
||||||
|
|
||||||
|
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
|
||||||
|
NAPI_CALL(env, napi_get_value_bool(env, argv[1], &resolution));
|
||||||
|
if (resolution) {
|
||||||
|
NAPI_CALL(env, napi_resolve_deferred(env, deferred, argv[0]));
|
||||||
|
} else {
|
||||||
|
NAPI_CALL(env, napi_reject_deferred(env, deferred, argv[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
deferred = NULL;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
napi_value isPromise(napi_env env, napi_callback_info info) {
|
||||||
|
napi_value promise, result;
|
||||||
|
size_t argc = 1;
|
||||||
|
bool is_promise;
|
||||||
|
|
||||||
|
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &promise, NULL, NULL));
|
||||||
|
NAPI_CALL(env, napi_is_promise(env, promise, &is_promise));
|
||||||
|
NAPI_CALL(env, napi_get_boolean(env, is_promise, &result));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
|
||||||
|
napi_property_descriptor descriptors[] = {
|
||||||
|
DECLARE_NAPI_PROPERTY("createPromise", createPromise),
|
||||||
|
DECLARE_NAPI_PROPERTY("concludeCurrentPromise", concludeCurrentPromise),
|
||||||
|
DECLARE_NAPI_PROPERTY("isPromise", isPromise),
|
||||||
|
};
|
||||||
|
|
||||||
|
NAPI_CALL_RETURN_VOID(env, napi_define_properties(
|
||||||
|
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
|
||||||
|
}
|
||||||
|
|
||||||
|
NAPI_MODULE(addon, Init)
|
Loading…
x
Reference in New Issue
Block a user