iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🌊

Implementing Sleep in WebAssembly: Rust Journey (8)

に公開

seems like wasm-timer::Delay can be used

Related Articles:

Previous articles in the Rust Chronicles series

I wonder how I can distinguish if something is usable in Wasm.

------------------- ↓ Introduction starts here ↓-------------------

To create a sample of asynchronous processing, I wanted to represent time-delayed operations by sleeping for a random duration on WebAssembly.
Is this what it looks like in code?

use std::time::Duration;
use std::{thread, time};
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn sleep_millis(numbers: u16) {
    let millis: u64 = u64::from(numbers);
    let ten_millis: Duration = time::Duration::from_millis(millis);
    thread::sleep(ten_millis);
}

When I run this,

1a8d8036:0x15b9 Uncaught (in promise) RuntimeError: unreachable
    at <anonymous>:wasm-function[5]:0x15b9
    at <anonymous>:wasm-function[13]:0x1876
    at <anonymous>:wasm-function[11]:0x1823
    at <anonymous>:wasm-function[12]:0x1851
    at sleep_millis (<anonymous>:wasm-function[7]:0x1710)
    at Object.sleep_millis (index.js:8)
    at App.svelte:7
    at Generator.next (<anonymous>)
    at App.svelte:1
    at new Promise (<anonymous>)

If I remove the sleep line, the error disappears.

(゚_゚) Are you kidding me...

Looking into it, it works fine when running in Rust, but it seems impossible to simply use sleep when running on WebAssembly.

There were even people doing acrobatic things like loading JavaScript.
That's fine for a sample, but it's not realistic for actual use.

When I looked for something good, I found something that seems quite usable.

That is

wasm-timer::Delay

ヾ(・ω<)ノ" 三三三● ⅱⅲ Rolling♪

------------------- ↓ Main content starts here ↓-------------------

Creating the project

Create a project from a template with a combination of rollup.js + WebAssembly.
The template's package versions are outdated, so I will update them to the latest.

cargo install cargo-edit wasm-pack
npx degit wasm-tool/rollup-plugin-rust/example wasm-sleep
cd wasm-sleep
npx npm-check-updates -u
npm i
cargo upgrade
cargo check

Installing additional packages

  • js-sys
    • Access parameters on the JavaScript side
    • Necessary for asynchronous processing
  • wasm-bindgen-futures
    • Bridges Rust Futures and JavaScript Promises
    • Necessary for asynchronous operations
  • wasm-timer
    • Timer-related package
  • parking_lot
    • Cuts off unnecessary code generated when using timers
cargo add js-sys wasm-bindgen-futures wasm-timer
cargo add parking_lot --features wasm-bindgen

Creating a timer function

Implementation to take input in msec and return a Promise

./src/lib.rs
use std::time::Duration;
use wasm_bindgen::prelude::*;
use wasm_timer::Delay;

#[wasm_bindgen]
pub async fn sleep_millis(numbers: u16) -> js_sys::Promise {
  let millis: u64 = u64::from(numbers);
  Delay::new(Duration::from_millis(millis)).await.unwrap();

  let promise = js_sys::Promise::resolve(&numbers.into());
  return promise;
}

Front-end implementation

Prepare the front-end to verify the sleep operation.
By displaying the execution time in the console, check if it stops for 5 seconds with sleep_millis.

./src/main.mjs
import wasm from "../Cargo.toml";

(async () => {
    const modules = await wasm()
    console.time("sleep_millis")
    await modules.sleep_millis(5000)
    console.timeEnd("sleep_millis")
})()

export default wasm

Adjusting the template side

Adjusting rollup.js settings

Finally, adjust the build tool settings.
Since I prepared code for the front-end, change the Cargo.toml call.

rollup.config.js
import rust from "@wasm-tool/rollup-plugin-rust";

export default {
    input: {
-        example: "Cargo.toml",
+        example: "./src/main.mjs",
    },
    output: {
        dir: "dist/js",
-        format: "iife",
        sourcemap: true,
    },
    plugins: [
        rust({
            serverPath: "js/",
        }),
    ],
};

Adjusting HTML

To support ES6 in HTML,
set the script tag to call as a module

./dist/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
-    <script src="js/example.js"></script>
+    <script src="js/example.js" type="module"></script>
  </body>
</html>

Build

npm run build
npx http-server dist

Execution

Access via browser, and the following will appear in the console after 5 seconds.

|・∀・| Halt!

If you remove await, it becomes an asynchronous timer.
Is it like JS's Timeout?

------------------- ↓ Epilogue starts here ↓-------------------

What is import * as __wbg_star0 from 'env'?

A mysterious error occurred

There was a warning on the Rollup side

Looking into it, there is a strange string at the beginning of the generated JavaScript file.
It seems wasm-pack wrote it out.

dist/js/bundle.js
import * as __wbg_star0 from 'env';

🤔 Hmm

I looked into it further, and it seems you just need to add the following package.

Cargo.toml
[dependencies]
・・・
parking_lot = { version = "0.11.1", features = ["wasm-bindgen"]}

Somehow it worked.

Reference:
https://github.com/rustwasm/wasm-bindgen/issues/2215#issuecomment-796244209

Discussion