API Usage
Important
Ensure you are using a supported version of the tools and engines as specified in the Support Matrix, and has read through the required Setups
This tutorial covers basic usage. For detailed technical reference please refer to the API Doc on docs.rs
Creating the thread dispatcher
The thread dispatcher is its own “thread” that allows new threads to be spawned without the need to rely on the JS event loop. You may read more about this design in the Design Blog
The thread dispatcher is initialized in 2 phases:
- Call one of the
init_bg_*functions to create an instance ofThreadDispatcherInit. - Use the JS Event Loop to wait for the thread dispatcher to be ready.
The
spawn()API only works after the thread dispatcher isawait-ed to be ready to prevent dead locks trying to join a thread before the thread dispatcher is ready.
#![allow(unused)]
fn main() {
// if you use wasm-pack build -t no-modules ...
let init = wasm_bindgen_spawn::init_bg_no_modules(bg_script, wasm_bindgen::module());
// if you use wasm-pack build -t web ...
let init = wasm_bindgen_spawn::init_bg_web(bg_script, wasm_bindgen::module());
}
Note
Currently the
init_bg_*function accepts the bindgen script from either theno-modulesor thewebtarget inwasm-pack. If your project uses another target, refer to Wasm-pack Target Setup. To setup the required scripts.The
wasm_bindgen::module()API is available in targets other thanbundleranddeno. Refer to the same setup guide for how to pass in the value from the JS side in these targets.
Next, create the dispatcher and wait for it to be ready.
If you use wasm-bindgen-futures, or js_sys::futures (with the WASM_BINDGEN_USE_JS_SYS=1 env flag or cfg flag),
you can use an async init function:
#![allow(unused)]
fn main() {
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub async fn init_thread_dispatcher(bg_script: JsValue) {
wasm_bindgen_spawn::init_bg_no_modules(bg_script)
.create_dispatcher().await.unwrap();
}
}
Then call and await this function in JS (see Wasm-pack Target Setup for full setup)
await wasm_bindgen.init_thread_dispatcher(bindgenScript)
If you don’t want to add additional dependencies other than wasm-bindgen,
you can use the create_dispatcher_promise() API instead, which uses js_sys::futures
internally to drive the dispatcher future with the JS event loop.
#![allow(unused)]
fn main() {
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn init_thread_dispatcher(bg_script: JsValue) -> JsValue /* Promise */{
// ^^ note this function is not async in rust
wasm_bindgen_spawn::init_bg_no_modules(bg_script)
.create_dispatcher_promise().into()
}
}
The JS side is identical
await wasm_bindgen.init_thread_dispatcher(bindgenScript)
Now the thread dispatcher is ready and you can spawn some threads!
Spawn and join
Spawn a thread with wasm_bindgen_spawn::spawn(), which has identical signature
to std::thread::spawn:
#![allow(unused)]
fn main() {
let thread = wasm_bindgen_spawn::spawn(move || {
/* here the code is now running inside a different worker JS context */
1
});
let output = thread.join().unwrap();
assert_eq!(output, 1);
}
Handle async code
You can use spawn_async to spawn a future as the “main function” of the thread.
The future is driven co-operatively with the JS event loop.
#![allow(unused)]
fn main() {
let thread = wasm_bindgen_spawn::spawn_async(move || async move {
// even though setTimeout does nothing, it is a minimal example
// that truly requires yielding to the JS event loop
let sleep = Function::new_no_args("return new Promise(r=>setTimeout(r,1000))");
let _ = sleep
.call0(&JsValue::undefined())
.unwrap()
.dyn_into::<Promise>()
.unwrap()
.await;
1
});
let output = thread.join().unwrap();
assert_eq!(output, 1);
}
Important
Before starting to use async threads, be sure to read read about the caveats, including why
spawn_asynctakesmove || async move {...}, in the Working with Async code chapter.
Non-blocking join
Similar to the JoinHandle in Rust standard library, the JoinHandle in this library
provides ways to perform non-blocking join of the thread.
#![allow(unused)]
fn main() {
let thread = wasm_bindgen_spawn::spawn(move || {
/* ... */
});
// use is_finished() to check if join will block
if thread.is_finished() {
// join will not block
let output = thread.join().unwrap();
}
}
The JoinHandle also implements IntoFuture for asynchronous join
#![allow(unused)]
fn main() {
let thread = wasm_bindgen_spawn::spawn(move || {
/* ... */
});
// asynchronously join the thread: the async runtime may do other things
// while the thread is not finished
let output = thread.await.unwrap();
}
Handle panic
The JoinHandle API captures panics in the thread and returns them as an Err
containing the opaque panic payload. The signature is identical to the standard library.
#![allow(unused)]
fn main() {
let thread = wasm_bindgen_spawn::spawn(move || {
/* ... */
});
match thread.join() {
Ok(x) => /* ... */,
Err(e /* Box<dyn Any + Send + 'static> */) => {
/* ... */
}
}
}
Caution
Panics are extremely tricky to handle, especially when different engines may exhibit different behaviors related to aborts and JS unhandled rejections, as well as panics in detached futures.
This library tries to make it really hard for panics to turn into uncontrollable failure if you do the right things. See the Working with Panic chapter for more information