mirror of
https://github.com/napi-rs/napi-rs.git
synced 2026-02-01 16:41:24 +00:00
feat(napi): provide PromiseRaw for non-await scenario (#2168)
This commit is contained in:
parent
a4cd94ea30
commit
bc9e931a4e
@ -1,3 +1,4 @@
|
||||
use bindgen_prelude::PromiseRaw;
|
||||
use napi::threadsafe_function::*;
|
||||
use napi::*;
|
||||
|
||||
@ -22,7 +23,7 @@ impl Task for BufferLength {
|
||||
}
|
||||
|
||||
#[js_function(1)]
|
||||
fn bench_async_task(ctx: CallContext) -> Result<JsObject> {
|
||||
fn bench_async_task(ctx: CallContext) -> Result<PromiseRaw<JsNumber>> {
|
||||
let n = ctx.get::<JsBuffer>(0)?;
|
||||
let task = BufferLength(n.into_ref()?);
|
||||
let async_promise = ctx.env.spawn(task)?;
|
||||
|
||||
@ -213,6 +213,7 @@ static KNOWN_TYPES: Lazy<HashMap<&'static str, (&'static str, bool, bool)>> = La
|
||||
("Either26", ("{} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {}", false, true)),
|
||||
("external", ("object", false, false)),
|
||||
("Promise", ("Promise<{}>", false, false)),
|
||||
("PromiseRaw", ("PromiseRaw<{}>", false, false)),
|
||||
("AbortSignal", ("AbortSignal", false, false)),
|
||||
("JsGlobal", ("typeof global", false, false)),
|
||||
("External", ("ExternalObject<{}>", false, false)),
|
||||
|
||||
@ -1,14 +1,13 @@
|
||||
use std::ffi::CString;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicU8, Ordering};
|
||||
|
||||
use crate::{
|
||||
bindgen_runtime::ToNapiValue, check_status, js_values::NapiValue, sys, Env, JsError, JsObject,
|
||||
Result, Task,
|
||||
};
|
||||
use crate::bindgen_runtime::PromiseRaw;
|
||||
use crate::{bindgen_runtime::ToNapiValue, check_status, sys, Env, JsError, Result, Task};
|
||||
|
||||
struct AsyncWork<T: Task> {
|
||||
inner_task: T,
|
||||
@ -18,7 +17,7 @@ struct AsyncWork<T: Task> {
|
||||
status: Rc<AtomicU8>,
|
||||
}
|
||||
|
||||
pub struct AsyncWorkPromise {
|
||||
pub struct AsyncWorkPromise<T> {
|
||||
pub(crate) napi_async_work: sys::napi_async_work,
|
||||
raw_promise: sys::napi_value,
|
||||
pub(crate) deferred: sys::napi_deferred,
|
||||
@ -28,14 +27,15 @@ pub struct AsyncWorkPromise {
|
||||
/// 1: completed
|
||||
/// 2: canceled
|
||||
pub(crate) status: Rc<AtomicU8>,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl AsyncWorkPromise {
|
||||
pub fn promise_object(&self) -> JsObject {
|
||||
unsafe { JsObject::from_raw_unchecked(self.env, self.raw_promise) }
|
||||
impl<T> AsyncWorkPromise<T> {
|
||||
pub fn promise_object(&self) -> PromiseRaw<T> {
|
||||
PromiseRaw::new(self.env, self.raw_promise)
|
||||
}
|
||||
|
||||
pub fn cancel(&self) -> Result<()> {
|
||||
pub fn cancel(&mut self) -> Result<()> {
|
||||
// must be happened in the main thread, relaxed is enough
|
||||
self.status.store(2, Ordering::Relaxed);
|
||||
check_status!(unsafe { sys::napi_cancel_async_work(self.env, self.napi_async_work) })
|
||||
@ -46,7 +46,7 @@ pub fn run<T: Task>(
|
||||
env: sys::napi_env,
|
||||
task: T,
|
||||
abort_status: Option<Rc<AtomicU8>>,
|
||||
) -> Result<AsyncWorkPromise> {
|
||||
) -> Result<AsyncWorkPromise<T::JsValue>> {
|
||||
let mut raw_resource = ptr::null_mut();
|
||||
check_status!(unsafe { sys::napi_create_object(env, &mut raw_resource) })?;
|
||||
let mut raw_promise = ptr::null_mut();
|
||||
@ -85,6 +85,7 @@ pub fn run<T: Task>(
|
||||
deferred,
|
||||
env,
|
||||
status: task_status,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::future;
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use std::ptr;
|
||||
use std::sync::{
|
||||
@ -12,8 +13,26 @@ use tokio::sync::oneshot::{channel, Receiver, Sender};
|
||||
|
||||
use crate::{check_status, sys, Error, JsUnknown, NapiValue, Result, Status};
|
||||
|
||||
use super::{FromNapiValue, TypeName, ValidateNapiValue};
|
||||
use super::{FromNapiValue, ToNapiValue, TypeName, ValidateNapiValue};
|
||||
|
||||
/// The JavaScript Promise object representation
|
||||
///
|
||||
/// This `Promise<T>` can be awaited in the Rust
|
||||
/// THis `Promise<T>` can also be passed from `#[napi]` fn
|
||||
///
|
||||
/// example:
|
||||
///
|
||||
/// ```no_run
|
||||
/// #[napi]
|
||||
/// pub fn await_promise_in_rust(promise: Promise<u32>) {
|
||||
/// let value = promise.await.unwrap();
|
||||
///
|
||||
/// println!("{value}");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// But this `Promise<T>` can not be pass back to `JavaScript`.
|
||||
/// If you want to use raw JavaScript `Promise` API, you can use the [`PromiseRaw`](./PromiseRaw) instead.
|
||||
pub struct Promise<T: FromNapiValue> {
|
||||
value: Pin<Box<Receiver<*mut Result<T>>>>,
|
||||
aborted: Arc<AtomicBool>,
|
||||
@ -256,3 +275,258 @@ unsafe extern "C" fn catch_callback<T: FromNapiValue>(
|
||||
})))));
|
||||
this
|
||||
}
|
||||
|
||||
pub struct PromiseRaw<T> {
|
||||
pub(crate) inner: sys::napi_value,
|
||||
env: sys::napi_env,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> PromiseRaw<T> {
|
||||
pub(crate) fn new(env: sys::napi_env, inner: sys::napi_value) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
env,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromNapiValue> PromiseRaw<T> {
|
||||
/// Promise.then method
|
||||
pub fn then<Callback, U>(&mut self, cb: Callback) -> Result<PromiseRaw<U>>
|
||||
where
|
||||
U: ToNapiValue,
|
||||
Callback: FnOnce(T) -> Result<U>,
|
||||
{
|
||||
let mut then_fn = ptr::null_mut();
|
||||
let then_c_string = unsafe { CStr::from_bytes_with_nul_unchecked(b"then\0") };
|
||||
check_status!(unsafe {
|
||||
sys::napi_get_named_property(self.env, self.inner, then_c_string.as_ptr(), &mut then_fn)
|
||||
})?;
|
||||
let mut then_callback = ptr::null_mut();
|
||||
let rust_cb = Box::into_raw(Box::new(cb));
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_create_function(
|
||||
self.env,
|
||||
then_c_string.as_ptr(),
|
||||
4,
|
||||
Some(raw_promise_then_callback::<T, U, Callback>),
|
||||
rust_cb.cast(),
|
||||
&mut then_callback,
|
||||
)
|
||||
},
|
||||
"Create then function for PromiseRaw failed"
|
||||
)?;
|
||||
let mut new_promise = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_call_function(
|
||||
self.env,
|
||||
self.inner,
|
||||
then_fn,
|
||||
1,
|
||||
[then_callback].as_ptr(),
|
||||
&mut new_promise,
|
||||
)
|
||||
},
|
||||
"Call then callback on PromiseRaw failed"
|
||||
)?;
|
||||
|
||||
Ok(PromiseRaw::<U> {
|
||||
env: self.env,
|
||||
inner: new_promise,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Promise.catch method
|
||||
pub fn catch<E, U, Callback>(&mut self, cb: Callback) -> Result<PromiseRaw<U>>
|
||||
where
|
||||
E: FromNapiValue,
|
||||
U: ToNapiValue,
|
||||
Callback: FnOnce(E) -> Result<U>,
|
||||
{
|
||||
let mut catch_fn = ptr::null_mut();
|
||||
check_status!(unsafe {
|
||||
sys::napi_get_named_property(
|
||||
self.env,
|
||||
self.inner,
|
||||
"catch\0".as_ptr().cast(),
|
||||
&mut catch_fn,
|
||||
)
|
||||
})?;
|
||||
let mut catch_callback = ptr::null_mut();
|
||||
let rust_cb = Box::into_raw(Box::new(cb));
|
||||
check_status!(unsafe {
|
||||
sys::napi_create_function(
|
||||
self.env,
|
||||
"catch\0".as_ptr().cast(),
|
||||
5,
|
||||
Some(raw_promise_catch_callback::<E, U, Callback>),
|
||||
rust_cb.cast(),
|
||||
&mut catch_callback,
|
||||
)
|
||||
})?;
|
||||
let mut new_promise = ptr::null_mut();
|
||||
check_status!(unsafe {
|
||||
sys::napi_call_function(
|
||||
self.env,
|
||||
self.inner,
|
||||
catch_fn,
|
||||
1,
|
||||
[catch_callback].as_mut_ptr().cast(),
|
||||
&mut new_promise,
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(PromiseRaw::<U> {
|
||||
env: self.env,
|
||||
inner: new_promise,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Convert `PromiseRaw<T>` to `Promise<T>`
|
||||
///
|
||||
/// So you can await the Promise in Rust
|
||||
pub fn into_sendable_promise(self) -> Result<Promise<T>> {
|
||||
unsafe { Promise::from_napi_value(self.env, self.inner) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromNapiValue> TypeName for PromiseRaw<T> {
|
||||
fn type_name() -> &'static str {
|
||||
"Promise"
|
||||
}
|
||||
|
||||
fn value_type() -> crate::ValueType {
|
||||
crate::ValueType::Object
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromNapiValue> ValidateNapiValue for PromiseRaw<T> {
|
||||
unsafe fn validate(
|
||||
env: napi_sys::napi_env,
|
||||
napi_val: napi_sys::napi_value,
|
||||
) -> Result<napi_sys::napi_value> {
|
||||
Promise::<T>::validate(env, napi_val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromNapiValue for PromiseRaw<T> {
|
||||
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result<Self> {
|
||||
Ok(PromiseRaw {
|
||||
inner: napi_val,
|
||||
env,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ToNapiValue for PromiseRaw<T> {
|
||||
unsafe fn to_napi_value(_env: napi_sys::napi_env, val: Self) -> Result<napi_sys::napi_value> {
|
||||
Ok(val.inner)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn raw_promise_then_callback<T, U, Cb>(
|
||||
env: sys::napi_env,
|
||||
cbinfo: sys::napi_callback_info,
|
||||
) -> sys::napi_value
|
||||
where
|
||||
T: FromNapiValue,
|
||||
U: ToNapiValue,
|
||||
Cb: FnOnce(T) -> Result<U>,
|
||||
{
|
||||
match handle_then_callback::<T, U, Cb>(env, cbinfo) {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
let code = CString::new(err.status.as_ref()).unwrap();
|
||||
let msg = CString::new(err.reason).unwrap();
|
||||
unsafe { sys::napi_throw_error(env, code.as_ptr(), msg.as_ptr()) };
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_then_callback<T, U, Cb>(
|
||||
env: sys::napi_env,
|
||||
cbinfo: sys::napi_callback_info,
|
||||
) -> Result<sys::napi_value>
|
||||
where
|
||||
T: FromNapiValue,
|
||||
U: ToNapiValue,
|
||||
Cb: FnOnce(T) -> Result<U>,
|
||||
{
|
||||
let mut callback_values = [ptr::null_mut()];
|
||||
let mut rust_cb = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_get_cb_info(
|
||||
env,
|
||||
cbinfo,
|
||||
&mut 1,
|
||||
callback_values.as_mut_ptr(),
|
||||
ptr::null_mut(),
|
||||
&mut rust_cb,
|
||||
)
|
||||
},
|
||||
"Get callback info from then callback failed"
|
||||
)?;
|
||||
let then_value: T = unsafe { FromNapiValue::from_napi_value(env, callback_values[0]) }?;
|
||||
let cb: Box<Cb> = unsafe { Box::from_raw(rust_cb.cast()) };
|
||||
|
||||
unsafe { U::to_napi_value(env, cb(then_value)?) }
|
||||
}
|
||||
|
||||
unsafe extern "C" fn raw_promise_catch_callback<E, U, Cb>(
|
||||
env: sys::napi_env,
|
||||
cbinfo: sys::napi_callback_info,
|
||||
) -> sys::napi_value
|
||||
where
|
||||
E: FromNapiValue,
|
||||
U: ToNapiValue,
|
||||
Cb: FnOnce(E) -> Result<U>,
|
||||
{
|
||||
match handle_catch_callback::<E, U, Cb>(env, cbinfo) {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
let code = CString::new(err.status.as_ref()).unwrap();
|
||||
let msg = CString::new(err.reason).unwrap();
|
||||
unsafe { sys::napi_throw_error(env, code.as_ptr(), msg.as_ptr()) };
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_catch_callback<E, U, Cb>(
|
||||
env: sys::napi_env,
|
||||
cbinfo: sys::napi_callback_info,
|
||||
) -> Result<sys::napi_value>
|
||||
where
|
||||
E: FromNapiValue,
|
||||
U: ToNapiValue,
|
||||
Cb: FnOnce(E) -> Result<U>,
|
||||
{
|
||||
let mut callback_values = [ptr::null_mut(); 1];
|
||||
let mut rust_cb = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_get_cb_info(
|
||||
env,
|
||||
cbinfo,
|
||||
&mut 1,
|
||||
callback_values.as_mut_ptr(),
|
||||
ptr::null_mut(),
|
||||
&mut rust_cb,
|
||||
)
|
||||
},
|
||||
"Get callback info from catch callback failed"
|
||||
)?;
|
||||
let catch_value: E = unsafe { FromNapiValue::from_napi_value(env, callback_values[0]) }?;
|
||||
let cb: Box<Cb> = unsafe { Box::from_raw(rust_cb.cast()) };
|
||||
|
||||
unsafe { U::to_napi_value(env, cb(catch_value)?) }
|
||||
}
|
||||
|
||||
@ -149,10 +149,10 @@ impl<T: Task> ToNapiValue for AsyncTask<T> {
|
||||
abort_controller
|
||||
.raw_deferred
|
||||
.store(async_promise.deferred, Ordering::Relaxed);
|
||||
Ok(async_promise.promise_object().0.value)
|
||||
Ok(async_promise.promise_object().inner)
|
||||
} else {
|
||||
let async_promise = async_work::run(env, val.inner, None)?;
|
||||
Ok(async_promise.promise_object().0.value)
|
||||
Ok(async_promise.promise_object().inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,8 @@ use serde::Serialize;
|
||||
use crate::async_cleanup_hook::AsyncCleanupHook;
|
||||
#[cfg(feature = "napi5")]
|
||||
use crate::bindgen_runtime::FunctionCallContext;
|
||||
#[cfg(all(feature = "tokio_rt", feature = "napi4"))]
|
||||
use crate::bindgen_runtime::PromiseRaw;
|
||||
#[cfg(feature = "napi4")]
|
||||
use crate::bindgen_runtime::ToNapiValue;
|
||||
use crate::bindgen_runtime::{FromNapiValue, Function, JsValuesTupleIntoVec, Unknown};
|
||||
@ -998,7 +1000,7 @@ impl Env {
|
||||
}
|
||||
|
||||
/// Run [Task](./trait.Task.html) in libuv thread pool, return [AsyncWorkPromise](./struct.AsyncWorkPromise.html)
|
||||
pub fn spawn<T: 'static + Task>(&self, task: T) -> Result<AsyncWorkPromise> {
|
||||
pub fn spawn<T: 'static + Task>(&self, task: T) -> Result<AsyncWorkPromise<T::JsValue>> {
|
||||
async_work::run(self.0, task, None)
|
||||
}
|
||||
|
||||
@ -1102,6 +1104,7 @@ impl Env {
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "tokio_rt", feature = "napi4"))]
|
||||
#[deprecated(since = "3.0.0", note = "Please use `Env::spawn_future` instead")]
|
||||
pub fn execute_tokio_future<
|
||||
T: 'static + Send,
|
||||
V: 'static + ToNapiValue,
|
||||
@ -1122,20 +1125,43 @@ impl Env {
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "tokio_rt", feature = "napi4"))]
|
||||
/// Spawn a future, return a JavaScript Promise which takes the result of the future
|
||||
pub fn spawn_future<
|
||||
T: 'static + Send + ToNapiValue,
|
||||
F: 'static + Send + Future<Output = Result<T>>,
|
||||
>(
|
||||
&self,
|
||||
fut: F,
|
||||
) -> Result<JsObject> {
|
||||
) -> Result<PromiseRaw<T>> {
|
||||
use crate::tokio_runtime;
|
||||
|
||||
let promise = tokio_runtime::execute_tokio_future(self.0, fut, |env, val| unsafe {
|
||||
ToNapiValue::to_napi_value(env, val)
|
||||
})?;
|
||||
|
||||
Ok(unsafe { JsObject::from_raw_unchecked(self.0, promise) })
|
||||
Ok(PromiseRaw::new(self.0, promise))
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "tokio_rt", feature = "napi4"))]
|
||||
/// Spawn a future with a callback
|
||||
/// So you can access the `Env` and resolved value after the future completed
|
||||
pub fn spawn_future_with_callback<
|
||||
T: 'static + Send + ToNapiValue,
|
||||
F: 'static + Send + Future<Output = Result<T>>,
|
||||
R: 'static + FnOnce(&mut Env, &mut T) -> Result<()>,
|
||||
>(
|
||||
&self,
|
||||
fut: F,
|
||||
callback: R,
|
||||
) -> Result<PromiseRaw<T>> {
|
||||
use crate::tokio_runtime;
|
||||
|
||||
let promise = tokio_runtime::execute_tokio_future(self.0, fut, move |env, mut val| unsafe {
|
||||
callback(&mut Env::from_raw(env), &mut val)?;
|
||||
ToNapiValue::to_napi_value(env, val)
|
||||
})?;
|
||||
|
||||
Ok(PromiseRaw::new(self.0, promise))
|
||||
}
|
||||
|
||||
/// Creates a deferred promise, which can be resolved or rejected from a background thread.
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
use std::convert::TryInto;
|
||||
|
||||
use napi::{
|
||||
CallContext, Env, Error, JsBuffer, JsBufferValue, JsNumber, JsObject, Ref, Result, Task,
|
||||
bindgen_prelude::PromiseRaw, CallContext, Env, Error, JsBuffer, JsBufferValue, JsNumber,
|
||||
JsObject, Ref, Result, Task,
|
||||
};
|
||||
|
||||
struct ComputeFib {
|
||||
@ -35,7 +36,7 @@ fn fibonacci_native(n: u32) -> u32 {
|
||||
}
|
||||
|
||||
#[js_function(1)]
|
||||
fn test_spawn_thread(ctx: CallContext) -> Result<JsObject> {
|
||||
fn test_spawn_thread(ctx: CallContext) -> Result<PromiseRaw<JsNumber>> {
|
||||
let n = ctx.get::<JsNumber>(0)?;
|
||||
let task = ComputeFib::new(n.try_into()?);
|
||||
let async_promise = ctx.env.spawn(task)?;
|
||||
@ -67,7 +68,7 @@ impl Task for CountBufferLength {
|
||||
env.create_uint32(output as _)
|
||||
}
|
||||
|
||||
fn reject(&mut self, env: Env, err: Error) -> Result<Self::JsValue> {
|
||||
fn reject(&mut self, _env: Env, err: Error) -> Result<Self::JsValue> {
|
||||
Err(err)
|
||||
}
|
||||
|
||||
@ -78,7 +79,7 @@ impl Task for CountBufferLength {
|
||||
}
|
||||
|
||||
#[js_function(1)]
|
||||
fn test_spawn_thread_with_ref(ctx: CallContext) -> Result<JsObject> {
|
||||
fn test_spawn_thread_with_ref(ctx: CallContext) -> Result<PromiseRaw<JsNumber>> {
|
||||
let n = ctx.get::<JsBuffer>(0)?.into_ref()?;
|
||||
let task = CountBufferLength::new(n);
|
||||
let async_work_promise = ctx.env.spawn(task)?;
|
||||
|
||||
@ -338,6 +338,8 @@ Generated by [AVA](https://avajs.dev).
|
||||
␊
|
||||
export declare function callbackReturnPromiseAndSpawn(jsFunc: (arg0: string) => Promise<string>): Promise<string>␊
|
||||
␊
|
||||
export declare function callCatchOnPromise(input: PromiseRaw<number>): PromiseRaw<string>␊
|
||||
␊
|
||||
export declare function callFunction(cb: () => number): number␊
|
||||
␊
|
||||
export declare function callFunctionWithArg(cb: (arg0: number, arg1: number) => number, arg0: number, arg1: number): number␊
|
||||
@ -346,6 +348,8 @@ Generated by [AVA](https://avajs.dev).
|
||||
␊
|
||||
export declare function callLongThreadsafeFunction(tsfn: (err: Error | null, arg: number) => unknown): void␊
|
||||
␊
|
||||
export declare function callThenOnPromise(input: PromiseRaw<number>): PromiseRaw<string>␊
|
||||
␊
|
||||
export declare function callThreadsafeFunction(tsfn: (err: Error | null, arg: number) => unknown): void␊
|
||||
␊
|
||||
export declare function captureErrorInCallback(cb1: () => void, cb2: (arg0: Error) => void): void␊
|
||||
|
||||
Binary file not shown.
@ -183,6 +183,8 @@ import {
|
||||
panicInAsync,
|
||||
CustomStruct,
|
||||
uInit8ArrayFromString,
|
||||
callThenOnPromise,
|
||||
callCatchOnPromise,
|
||||
} from '../index.cjs'
|
||||
|
||||
import { test } from './test.framework.js'
|
||||
@ -491,6 +493,13 @@ Napi4Test('callback function return Promise and spawn', async (t) => {
|
||||
t.is(finalReturn, 'Hello world 😼')
|
||||
})
|
||||
|
||||
test('promise', async (t) => {
|
||||
const res = await callThenOnPromise(Promise.resolve(1))
|
||||
t.is(res, '1')
|
||||
const cat = await callCatchOnPromise(Promise.reject('cat'))
|
||||
t.is(cat, 'cat')
|
||||
})
|
||||
|
||||
test('object', (t) => {
|
||||
t.deepEqual(listObjKeys({ name: 'John Doe', age: 20 }), ['name', 'age'])
|
||||
t.deepEqual(createObj(), { test: 1 })
|
||||
|
||||
@ -434,10 +434,12 @@ module.exports.call1 = nativeBinding.call1
|
||||
module.exports.call2 = nativeBinding.call2
|
||||
module.exports.callbackReturnPromise = nativeBinding.callbackReturnPromise
|
||||
module.exports.callbackReturnPromiseAndSpawn = nativeBinding.callbackReturnPromiseAndSpawn
|
||||
module.exports.callCatchOnPromise = nativeBinding.callCatchOnPromise
|
||||
module.exports.callFunction = nativeBinding.callFunction
|
||||
module.exports.callFunctionWithArg = nativeBinding.callFunctionWithArg
|
||||
module.exports.callFunctionWithArgAndCtx = nativeBinding.callFunctionWithArgAndCtx
|
||||
module.exports.callLongThreadsafeFunction = nativeBinding.callLongThreadsafeFunction
|
||||
module.exports.callThenOnPromise = nativeBinding.callThenOnPromise
|
||||
module.exports.callThreadsafeFunction = nativeBinding.callThreadsafeFunction
|
||||
module.exports.captureErrorInCallback = nativeBinding.captureErrorInCallback
|
||||
module.exports.chronoDateAdd1Minute = nativeBinding.chronoDateAdd1Minute
|
||||
|
||||
@ -328,6 +328,8 @@ export declare function callbackReturnPromise<T>(functionInput: () => T | Promis
|
||||
|
||||
export declare function callbackReturnPromiseAndSpawn(jsFunc: (arg0: string) => Promise<string>): Promise<string>
|
||||
|
||||
export declare function callCatchOnPromise(input: PromiseRaw<number>): PromiseRaw<string>
|
||||
|
||||
export declare function callFunction(cb: () => number): number
|
||||
|
||||
export declare function callFunctionWithArg(cb: (arg0: number, arg1: number) => number, arg0: number, arg1: number): number
|
||||
@ -336,6 +338,8 @@ export declare function callFunctionWithArgAndCtx(ctx: Animal, cb: (arg: string)
|
||||
|
||||
export declare function callLongThreadsafeFunction(tsfn: (err: Error | null, arg: number) => unknown): void
|
||||
|
||||
export declare function callThenOnPromise(input: PromiseRaw<number>): PromiseRaw<string>
|
||||
|
||||
export declare function callThreadsafeFunction(tsfn: (err: Error | null, arg: number) => unknown): void
|
||||
|
||||
export declare function captureErrorInCallback(cb1: () => void, cb2: (arg0: Error) => void): void
|
||||
|
||||
@ -78,7 +78,7 @@ fn callback_return_promise<T: Fn() -> Result<JsUnknown>>(
|
||||
pub fn callback_return_promise_and_spawn<F: Fn(String) -> Result<Promise<String>>>(
|
||||
env: Env,
|
||||
js_func: F,
|
||||
) -> napi::Result<Object> {
|
||||
) -> napi::Result<PromiseRaw<String>> {
|
||||
let promise = js_func("Hello".to_owned())?;
|
||||
env.spawn_future(async move {
|
||||
let resolved = promise.await?;
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use napi::{
|
||||
bindgen_prelude::{ClassInstance, Function, FunctionRef},
|
||||
bindgen_prelude::{ClassInstance, Function, FunctionRef, PromiseRaw},
|
||||
threadsafe_function::ThreadsafeFunctionCallMode,
|
||||
Env, Error, JsObject, Result, Status,
|
||||
Env, Error, Result, Status,
|
||||
};
|
||||
|
||||
use crate::class::Animal;
|
||||
@ -48,9 +48,9 @@ pub fn call_function_with_arg(cb: Function<(u32, u32), u32>, arg0: u32, arg1: u3
|
||||
}
|
||||
|
||||
#[napi(ts_return_type = "Promise<void>")]
|
||||
pub fn create_reference_on_function(env: Env, cb: Function<(), ()>) -> Result<JsObject> {
|
||||
pub fn create_reference_on_function(env: Env, cb: Function<(), ()>) -> Result<PromiseRaw<()>> {
|
||||
let reference = cb.create_ref()?;
|
||||
env.execute_tokio_future(
|
||||
env.spawn_future_with_callback(
|
||||
async {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||
Ok(())
|
||||
|
||||
@ -5,3 +5,13 @@ pub async fn async_plus_100(p: Promise<u32>) -> Result<u32> {
|
||||
let v = p.await?;
|
||||
Ok(v + 100)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn call_then_on_promise(mut input: PromiseRaw<u32>) -> Result<PromiseRaw<String>> {
|
||||
input.then(|v| Ok(format!("{}", v)))
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn call_catch_on_promise(mut input: PromiseRaw<u32>) -> Result<PromiseRaw<String>> {
|
||||
input.catch(|e: String| Ok(format!("{}", e)))
|
||||
}
|
||||
|
||||
@ -98,7 +98,10 @@ pub fn tsfn_call_with_callback(tsfn: ThreadsafeFunction<(), String>) -> napi::Re
|
||||
}
|
||||
|
||||
#[napi(ts_return_type = "Promise<void>")]
|
||||
pub fn tsfn_async_call(env: Env, func: Function<(u32, u32, u32), String>) -> napi::Result<Object> {
|
||||
pub fn tsfn_async_call(
|
||||
env: Env,
|
||||
func: Function<(u32, u32, u32), String>,
|
||||
) -> napi::Result<PromiseRaw<()>> {
|
||||
let tsfn = func.build_threadsafe_function().build()?;
|
||||
|
||||
env.spawn_future(async move {
|
||||
|
||||
@ -42,7 +42,7 @@ pub struct Room {
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn test_async(env: Env) -> napi::Result<napi::JsObject> {
|
||||
pub fn test_async(env: Env) -> napi::Result<napi::bindgen_prelude::PromiseRaw<String>> {
|
||||
let data = serde_json::json!({
|
||||
"findFirstBooking": {
|
||||
"id": "ckovh15xa104945sj64rdk8oas",
|
||||
@ -64,11 +64,11 @@ pub fn test_async(env: Env) -> napi::Result<napi::JsObject> {
|
||||
"room": { "id": "ckovh15xa104955sj6r2tqaw1c", "name": "38683b87f2664" }
|
||||
}
|
||||
});
|
||||
env.execute_tokio_future(
|
||||
env.spawn_future_with_callback(
|
||||
async move { Ok(serde_json::to_string(&data).unwrap()) },
|
||||
|env, res| {
|
||||
env.adjust_external_memory(res.len() as i64)?;
|
||||
env.create_string_from_std(res)
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user