From f7d791fac7bfc9f9a4fce43c05137e960cd015d6 Mon Sep 17 00:00:00 2001 From: sakex Date: Mon, 24 Jul 2023 22:10:51 +0200 Subject: [PATCH] async.md --- src/SUMMARY.md | 2 + .../server/files/index.html | 3 + src/webassembly/async.md | 75 +++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 src/webassembly/async.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index d531ba2d..d3930238 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -306,6 +306,8 @@ - [Expose user-defined Rust types](webassembly/expose-rust-type.md) - [Import user-defined Javascript types](webassembly/import-js-type.md) - [Borrow Checker](webassembly/borrow-checker.md) +- [Async](webassembly/async.md) + # Final Words diff --git a/src/rust-wasm-template/server/files/index.html b/src/rust-wasm-template/server/files/index.html index 18e222fd..5be9ee20 100644 --- a/src/rust-wasm-template/server/files/index.html +++ b/src/rust-wasm-template/server/files/index.html @@ -75,6 +75,9 @@ border-radius: 5px; font-weight: bold; cursor: pointer; + display: inline-block; + min-height: 1em; + min-width: 1em; } img { diff --git a/src/webassembly/async.md b/src/webassembly/async.md new file mode 100644 index 00000000..8eb2faa1 --- /dev/null +++ b/src/webassembly/async.md @@ -0,0 +1,75 @@ +# Async + +Rust methods in WebAssembly can be declared async. Once called, they will be scheduled on the browser's event loop. +An event handler can for instance be implemented with a tokio channel. + +Instead of `tokio::spawn`, `wasm_bindgen` provides `wasm_bindgen_futures::spawn_local`. + +Let's create a class that waits for messages on a channel to rotate an HTML element: + +```rust +#[derive(Debug)] +enum RotateSide { + Left, + Right, +} + +#[wasm_bindgen] +pub struct Rotator { + sender: Sender, +} + +#[wasm_bindgen] +impl Rotator { + #[wasm_bindgen(constructor)] + pub fn new(element: web_sys::HtmlElement) -> Rotator { + let (sender, mut receiver) = channel::(1); + spawn_local(async move { + let mut rotation = 0; + while let Some(rotate_side) = receiver.recv().await { + log(&format!("Rotation: {}", rotation)); + match rotate_side { + RotateSide::Left => rotation -= 45, + RotateSide::Right => rotation += 45, + } + element.set_inner_html(&rotation.to_string()); + let style = element.style(); + style + .set_property("transform", &format!("rotate({rotation}deg)")) + .expect("Failed to rotate"); + } + }); + Rotator { sender } + } + + #[wasm_bindgen] + pub async fn rotate(&self, msg: String) -> Result<(), JsValue> { + let rotate_side = match msg.as_str() { + "ArrowLeft" => RotateSide::Left, + "ArrowRight" => RotateSide::Right, + _ => return Ok(()), + }; + self.sender + .send(rotate_side) + .await + .map_err(|e| JsValue::from_str(&format!("Receiver dropped {:?}", e))) + } +} +``` + +Let's call it from Javascript + +```javascript +import init, {Rotator} from '/wasm/project.js'; + +(async () => { + // Run the init method to initiate the WebAssembly module. + await init(); + const wasmoutput = document.querySelector('#wasmoutput'); + const rotator = new Rotator(wasmoutput); + document.body.addEventListener('keydown', async (e) => { + await rotator.rotate(e.key); + }); +})(); + +```