Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

  1. Call one of the init_bg_* functions to create an instance of ThreadDispatcherInit.
  2. Use the JS Event Loop to wait for the thread dispatcher to be ready. The spawn() API only works after the thread dispatcher is await-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 the no-modules or the web target in wasm-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 than bundler and deno. 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_async takes move || 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