feat(napi): add from_napi_value for Ref<T: FromNapiValue> (#2603)

This commit is contained in:
LongYinan 2025-05-05 23:38:46 +08:00 committed by GitHub
parent d942a6bab9
commit bcf5e14aa8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 81 additions and 20 deletions

View File

@ -631,7 +631,7 @@ jobs:
with:
operating_system: freebsd
version: '14.2'
memory: 8G
memory: 12G
cpu_count: 3
environment_variables: 'DEBUG RUSTUP_IO_THREADS'
shell: bash

View File

@ -278,20 +278,19 @@ fn generator_next_fn<T: AsyncGenerator>(
};
let env = Env::from_raw(env);
let promise: crate::bindgen_runtime::PromiseRaw<'_, Option<T::Yield>> = env
.spawn_future_with_callback(item, |env, value| {
if let Some(v) = value {
let mut obj = Object::new(env.0)?;
obj.set("value", v)?;
obj.set("done", false)?;
Ok(obj)
} else {
let mut obj = Object::new(env.0)?;
obj.set("value", ())?;
obj.set("done", true)?;
Ok(obj)
}
})?;
let promise = env.spawn_future_with_callback(item, |env, value| {
if let Some(v) = value {
let mut obj = Object::new(env.0)?;
obj.set("value", v)?;
obj.set("done", false)?;
Ok(obj)
} else {
let mut obj = Object::new(env.0)?;
obj.set("value", ())?;
obj.set("done", true)?;
Ok(obj)
}
})?;
Ok(promise.inner)
}

View File

@ -1107,7 +1107,7 @@ impl Env {
/// 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,
T: 'static + Send,
V: ToNapiValue,
F: 'static + Send + Future<Output = Result<T>>,
R: 'static + FnOnce(Env, T) -> Result<V>,
@ -1115,7 +1115,7 @@ impl Env {
&self,
fut: F,
callback: R,
) -> Result<PromiseRaw<T>> {
) -> Result<PromiseRaw<V>> {
use crate::tokio_runtime;
let promise = tokio_runtime::execute_tokio_future(self.0, fut, move |env, val| unsafe {

View File

@ -19,7 +19,11 @@ unsafe impl<T> Sync for Ref<T> {}
impl<T: NapiRaw> Ref<T> {
pub fn new(env: &Env, value: &T) -> Result<Ref<T>> {
let mut raw_ref = ptr::null_mut();
check_status!(unsafe { sys::napi_create_reference(env.0, value.raw(), 1, &mut raw_ref) })?;
check_status!(
unsafe { sys::napi_create_reference(env.0, value.raw(), 1, &mut raw_ref) },
"Create napi_ref from {} failed",
std::any::type_name::<T>()
)?;
Ok(Ref {
raw_ref,
taken: false,
@ -66,6 +70,13 @@ impl<T: 'static + FromNapiMutRef> Ref<T> {
}
}
impl<T: FromNapiValue + NapiRaw> FromNapiValue for Ref<T> {
unsafe fn from_napi_value(env: sys::napi_env, value: sys::napi_value) -> Result<Self> {
let val = T::from_napi_value(env, value)?;
Ref::new(&Env::from_raw(env), &val)
}
}
impl<T: 'static> ToNapiValue for Ref<T> {
unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
let mut result = ptr::null_mut();

View File

@ -1,5 +1,6 @@
#![allow(clippy::single_component_path_imports)]
use std::convert::identity;
use std::marker::PhantomData;
use std::os::raw::c_void;
use std::ptr::{self, null_mut};
@ -492,7 +493,7 @@ impl<
"Receive value from threadsafe function sender failed",
)
})
.and_then(|ret| ret)
.and_then(identity)
}
}
@ -771,6 +772,10 @@ fn handle_call_js_cb_status(status: sys::napi_status, raw_env: sys::napi_env) {
}
}
/// This is a placeholder type that is used to indicate that the return value of a threadsafe function is unknown.
/// Use this type when you don't care about the return value of a threadsafe function.
///
/// And you can't get the value of it as well because it's just a placeholder.
pub struct UnknownReturnValue;
impl TypeName for UnknownReturnValue {

View File

@ -390,6 +390,8 @@ Generated by [AVA](https://avajs.dev).
export declare function call2(callback: (arg0: number, arg1: number) => number, arg1: number, arg2: number): number␊
export declare function callAsyncWithUnknownReturnValue(tsfn: ((err: Error | null, arg: number) => Ref<unknown>)): Promise<number>
export declare function callbackReturnPromise<T>(functionInput: () => T | Promise<T>, callback: (err: Error | null, result: T) => void): T | Promise<T>
export declare function callbackReturnPromiseAndSpawn(jsFunc: (arg0: string) => Promise<string>): Promise<string>

View File

@ -230,6 +230,7 @@ import {
getClassFromArray,
extendsJavascriptError,
shutdownRuntime,
callAsyncWithUnknownReturnValue,
} from '../index.cjs'
import { test } from './test.framework.js'
@ -1390,6 +1391,22 @@ Napi4Test('threadsafe function return Promise and await in Rust', async (t) => {
await new Promise((resolve) => setTimeout(resolve, 400))
})
Napi4Test('call async with unknown return value', async (t) => {
await new Promise<number>((resolve, reject) => {
return callAsyncWithUnknownReturnValue((err, value) => {
if (err) {
reject(err)
} else {
resolve(value)
t.is(value, 42)
return {}
}
}).then((result) => {
t.is(result, 110)
})
})
})
Napi4Test('object only from js', (t) => {
return new Promise((resolve, reject) => {
receiveObjectOnlyFromJs({

View File

@ -139,6 +139,7 @@ export const buildThreadsafeFunctionFromFunctionCalleeHandle = __napiModule.expo
export const call0 = __napiModule.exports.call0
export const call1 = __napiModule.exports.call1
export const call2 = __napiModule.exports.call2
export const callAsyncWithUnknownReturnValue = __napiModule.exports.callAsyncWithUnknownReturnValue
export const callbackReturnPromise = __napiModule.exports.callbackReturnPromise
export const callbackReturnPromiseAndSpawn = __napiModule.exports.callbackReturnPromiseAndSpawn
export const callCatchOnPromise = __napiModule.exports.callCatchOnPromise

View File

@ -163,6 +163,7 @@ module.exports.buildThreadsafeFunctionFromFunctionCalleeHandle = __napiModule.ex
module.exports.call0 = __napiModule.exports.call0
module.exports.call1 = __napiModule.exports.call1
module.exports.call2 = __napiModule.exports.call2
module.exports.callAsyncWithUnknownReturnValue = __napiModule.exports.callAsyncWithUnknownReturnValue
module.exports.callbackReturnPromise = __napiModule.exports.callbackReturnPromise
module.exports.callbackReturnPromiseAndSpawn = __napiModule.exports.callbackReturnPromiseAndSpawn
module.exports.callCatchOnPromise = __napiModule.exports.callCatchOnPromise

View File

@ -453,6 +453,7 @@ module.exports.buildThreadsafeFunctionFromFunctionCalleeHandle = nativeBinding.b
module.exports.call0 = nativeBinding.call0
module.exports.call1 = nativeBinding.call1
module.exports.call2 = nativeBinding.call2
module.exports.callAsyncWithUnknownReturnValue = nativeBinding.callAsyncWithUnknownReturnValue
module.exports.callbackReturnPromise = nativeBinding.callbackReturnPromise
module.exports.callbackReturnPromiseAndSpawn = nativeBinding.callbackReturnPromiseAndSpawn
module.exports.callCatchOnPromise = nativeBinding.callCatchOnPromise

View File

@ -352,6 +352,8 @@ export declare function call1(callback: (arg: number) => number, arg: number): n
export declare function call2(callback: (arg0: number, arg1: number) => number, arg1: number, arg2: number): number
export declare function callAsyncWithUnknownReturnValue(tsfn: ((err: Error | null, arg: number) => Ref<unknown>)): Promise<number>
export declare function callbackReturnPromise<T>(functionInput: () => T | Promise<T>, callback: (err: Error | null, result: T) => void): T | Promise<T>
export declare function callbackReturnPromiseAndSpawn(jsFunc: (arg0: string) => Promise<string>): Promise<string>

View File

@ -3,6 +3,7 @@ use std::{sync::Arc, thread, time::Duration};
use napi::{
bindgen_prelude::*,
threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode, UnknownReturnValue},
Ref,
};
use crate::class::Animal;
@ -170,6 +171,27 @@ pub async fn tsfn_return_promise_timeout(
}
}
#[napi]
pub fn call_async_with_unknown_return_value(
env: &Env,
tsfn: ThreadsafeFunction<u32, Ref<Unknown>>,
) -> Result<PromiseRaw<u32>> {
env.spawn_future_with_callback(
async move {
let return_value = tsfn.call_async(Ok(42)).await?;
Ok(return_value)
},
|env, mut value| {
let return_value = value.get_value(&env)?;
value.unref(&env)?;
match return_value.get_type()? {
ValueType::Object => Ok(110),
_ => Ok(100),
}
},
)
}
#[napi]
pub async fn tsfn_throw_from_js(tsfn: ThreadsafeFunction<u32, Promise<u32>>) -> napi::Result<u32> {
tsfn.call_async(Ok(42)).await?.await

View File

@ -42,7 +42,7 @@ pub struct Room {
}
#[napi]
pub fn test_async(env: &Env) -> napi::Result<napi::bindgen_prelude::PromiseRaw<String>> {
pub fn test_async(env: &Env) -> napi::Result<napi::bindgen_prelude::PromiseRaw<()>> {
let data = serde_json::json!({
"findFirstBooking": {
"id": "ckovh15xa104945sj64rdk8oas",