Is there a way to register & call a rust async function from javascript? #34
-
Hi, I'm hitting roadblock trying to call a rust async function from javascript. Similar to EsRuntime.set_function is there a way to register an async rust function? My use case is to invoke an async js function from rust which intern may invoke other async rust functions. i.e. Rust -> JS -> Rust -> JS Appreciate your help. |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 3 replies
-
Hi, Yes you can! use quickjs_runtime::esruntimebuilder::EsRuntimeBuilder;
use quickjs_runtime::esvalue::{EsPromise, EsValueConvertible};
use futures::executor::block_on;
use quickjs_runtime::esscript::EsScript;
async fn my_async_func_1(input: i32) -> i32 {
let two = my_async_func_2(input).await;
two * 2
}
async fn my_async_func_2(input: i32) -> i32 {
3 * input
}
fn main() {
let rt = EsRuntimeBuilder::new().build();
rt.set_function(vec!["my", "comp"], "funky", |_q_ctx, args| {
let input = args[0].get_i32();
// because we want to run an async func we want to return a Promise
// which needs a resolver which is a function which will run in a helper thread (async)
let ret_prom = EsPromise::new(move || { // move input here
// this function is the resolver, it runs in a separate helper thread(pool)
// this is the unfortunate part, we need to wait for the async result and block the helper thread
// i'm going to fix this in [#35](https://github.com/HiRoFa/quickjs_es_runtime/issues/35)
// after that you should be able to do .await here instead of block_on
let resolv_val = block_on(my_async_func_1(input));
Ok(resolv_val.to_es_value_facade())
});
Ok(ret_prom.to_es_value_facade())
}).ok().expect("set function failed");
let prom_esvf = rt.eval_sync(EsScript::new("test.js", "(my.comp.funky(4))")).ok().expect("script failed");
let res = prom_esvf.get_promise_result_sync();
assert!(res.is_ok());
assert_eq!(res.ok().unwrap().get_i32(), 24); // 4 * 3 * 2 = 24
} In #35 i'll also add this as a util to EsRuntime If anything is unclear please let me know! Thank you for using quicks_es_runtime! |
Beta Was this translation helpful? Give feedback.
-
Ok, i've updated the threading stuff in EsRuntime when 0.2.3 comes out or by linking to github directly you can use a much nicer working variant use quickjs_runtime::esruntimebuilder::EsRuntimeBuilder;
use quickjs_runtime::esvalue::{EsPromise, EsValueConvertible};
use quickjs_runtime::esscript::EsScript;
async fn my_async_func_1(input: i32) -> i32 {
let two = my_async_func_2(input).await;
two * 2
}
async fn my_async_func_2(input: i32) -> i32 {
3 * input
}
fn main() {
let rt = EsRuntimeBuilder::new().build();
rt.set_function(vec!["my", "comp"], "funky", |_q_ctx, args| {
let input = args[0].get_i32();
// because we want to run an async func we want to return a Promise
// which needs a resolver which is a function which will run in a helper thread (async)
let ret_prom = EsPromise::new_async(async move { // move input here
// this function is the resolver, it runs in a separate helper thread(pool)
let resolv_val = my_async_func_1(input).await;
Ok(resolv_val.to_es_value_facade())
});
Ok(ret_prom.to_es_value_facade())
}).ok().expect("set function failed");
let prom_esvf = rt.eval_sync(EsScript::new("test.js", "(my.comp.funky(4))")).ok().expect("script failed");
let res = prom_esvf.get_promise_result_sync();
assert!(res.is_ok());
assert_eq!(res.ok().unwrap().get_i32(), 24); // 4 * 3 * 2 = 24
} Cargo.toml [dependencies]
quickjs_runtime = {git = "https://github.com/HiRoFa/quickjs_es_runtime"} |
Beta Was this translation helpful? Give feedback.
-
well i made tokio full featured under #37 (i'll make an optional feature of that later) EsValueFacade should be Send, could you post a piece of you're code so i can see what's going on? |
Beta Was this translation helpful? Give feedback.
-
i modified the way EsValueFacade does get_promise_result, instead of making an async fn it now just return a Future (rust had an issue with &self still being borrowed because of the .await) i altered your code so it does not create a new rt in the async method, that rt would be dropped at the end of that async method and behaviour would be unpredictable, in my unit test there is an example where i use a Weak ref to keep a ref to the original runtime. Kind regards use quickjs_runtime::esruntimebuilder::EsRuntimeBuilder;
use quickjs_runtime::esvalue::{EsValueConvertible, EsPromise};
use quickjs_runtime::esscript::EsScript;
use quickjs_runtime::esruntime::EsRuntime;
use std::sync::Arc;
#[macro_use]
extern crate lazy_static;
lazy_static!{
static ref RT: Arc<EsRuntime> = EsRuntimeBuilder::new().build();
}
async fn my_async_func(input: i32) -> i32 {
if let Ok(promise) = RT
.eval(EsScript::new(
"return_3.js",
"new Promise((resolve) => {resolve(3)});",
))
.await
{
// tokio::time::sleep(Duration::from_millis(100)).await; // uncomment this line to see the tokio related error
// if let Ok(val) = promise.get_promise_result_sync() { // sync works
let fut = promise.get_promise_result();
let async_res= fut.await;
if let Ok(val) = async_res { // async complains about `&EsValueFacade` is not `Send`
return val.get_i32() * input;
}
}
return 0;
}
fn main() {
RT.set_function(vec!["my", "comp"], "funky", |_q_ctx, args| {
let input = args[0].get_i32();
let ret_prom = EsPromise::new_async(async move {
let resolved_val = my_async_func(input).await;
Ok(resolved_val.to_es_value_facade())
});
Ok(ret_prom.to_es_value_facade())
})
.ok()
.expect("set function failed");
let prom_esvf = RT
.eval_sync(EsScript::new(
"test.js",
r#"
const call_funky = async () => {
return await my.comp.funky(4);
};
call_funky();
"#,
))
.ok()
.expect("script failed");
let res = prom_esvf.get_promise_result_sync();
assert!(res.is_ok());
assert_eq!(res.ok().unwrap().get_i32(), 12);
} |
Beta Was this translation helpful? Give feedback.
i modified the way EsValueFacade does get_promise_result, instead of making an async fn it now just return a Future (rust had an issue with &self still being borrowed because of the .await)
i altered your code so it does not create a new rt in the async method, that rt would be dropped at the end of that async method and behaviour would be unpredictable, in my unit test there is an example where i use a Weak ref to keep a ref to the original runtime.
Kind regards