在做 wgpu Game of Life 時發現一件事:WASM 環境下的 Rust async 不需要 Tokio 或 async-std——瀏覽器的 event loop 就是 runtime。
在 native Rust,你需要一個 executor 來 poll futures。但在 WASM,wasm-bindgen-futures 把 Rust 的 Future 轉換成 JavaScript 的 Promise,交給瀏覽器的 event loop 來驅動:
#[wasm_bindgen]
pub async fn start(canvas_id: &str) {
// 每個 .await 都是把控制權交還給瀏覽器的 event loop
let adapter = instance.request_adapter(&options).await;
let (device, queue) = adapter.request_device(&desc, None).await;
}JavaScript 端看到的就是一個回傳 Promise 的函式:
await wasm.start("canvas");每次 .await 時,Rust future 暫停執行,控制權回到瀏覽器。當底層的 JS 操作(例如 WebGPU 的 requestAdapter)完成時,瀏覽器透過 microtask 觸發 Rust 的 waker,從暫停處繼續執行。
| Native | WASM | |
|---|---|---|
| Runtime | Tokio / async-std | 瀏覽器 event loop |
| Executor | Rust 端的 thread pool | JS microtask queue |
| Spawn | tokio::spawn(多執行緒) |
spawn_local(單執行緒) |
所以 Cargo.toml 只需要 wasm-bindgen-futures,不需要任何 Rust async runtime。