Rust async function stuck indefinitely when called from js
zlindner opened this issue · comments
Describe the bug
Async rust functions called from js seem to hang/get stuck forever when using tokio.
To Reproduce
Spawn tasks in a loop that execute a simple javascript script which calls a single function sleep
that is a rust async native function:
#[tokio::main]
async fn main() -> anyhow::Result<()> {
loop {
tokio::spawn(async move {
let mut context = Context::default();
context
.register_global_callable(
js_string!("sleep"),
0,
NativeFunction::from_async_fn(do_something_async),
)
.unwrap();
let script = r"
function test() {
sleep();
return true;
}";
context.eval(Source::from_bytes(script)).unwrap();
let result = context.eval(Source::from_bytes("test()")).unwrap();
println!("{:?}", result);
});
}
}
fn do_something_async(
_this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> impl Future<Output = JsResult<JsValue>> {
async move {
println!("Start sleeping...");
sleep(Duration::from_secs(1)).await;
println!("Done!");
Ok(JsValue::Boolean(true))
}
}
This results in the below output: (this is all the output I get, all 8 "Start sleeping..." lines get printed immediately then gets stuck forever)
Start sleeping...
Start sleeping...
Start sleeping...
Start sleeping...
Start sleeping...
Start sleeping...
Start sleeping...
Start sleeping...
Any ideas why this is the case? Not sure if I'm doing something wrong or if this is a bug. It seems to work fine if I either remove the tokio::spawn
altogether and just run in the main function, or await the JoinHandle
for the tasks.
Build environment (please complete the following information):
- OS: macOS
- Version: Tested both 0.17.3 and main branch, same behaviour
- Rustc version: rustc 1.75.0 (82e1608df 2023-12-21)
Remember that all tokio futures must be polled by a tokio executor to progress. The default job executor doesn't use an async executor, it only blocks on all future jobs.
If you need this functionality, you'd have to override the JobQueue
and use a thread-local tokio executor to push all futures forward.
Ok makes sense, thanks for the help :). Will try it out, if I get something working will create a PR adding an example.
Good! Also, remember to call run_jobs_async
to push the futures forward, or else the context will just evaluate the code without evaluating any of the futures and promises
Will close this since the original question was resolved.