Async sleep in Rust/wasm32?

Is there an async sleep anywhere in Promises and Futures - The `wasm-bindgen` Guide ?

Normally, I would use setTimeout() - Web APIs | MDN , but I am looking for a async rather than timeout based solution.

For what it's worth, you can pretty trivially convert a setTimeout() based sleep into something async.

use futures::channel::oneshot;
use std::time::Duration;

fn setTimeout(duration: Duration, callback: impl FnOnce()) {
    todo!();
}

async fn sleep(duration: Duration) {
    let (send, recv) = oneshot::channel();

    setTimeout(duration, move || {
        let _ = send.send(());
    });

    recv.await;
}

(playground)

4 Likes

@Michael-F-Bryan : Very cool technique. Can we do this without the channel ?

In JS, one technique for settimeout -> async sleep is:

// untested, copied from internet
waitFor = delay => new Promise(resolved => setTimeout(resolve, delay));
await waitFor(...);

I'm wondering if there is a 'direct' translation of this via wasm_bindgen_futures, which does not need the intermediate oneshot::channel.

I'm just using the channel as a cheap way to write a future - it's in no way necessary for this to work. If you squint, the sender.send() lets us delegate to Waker::wake() to tell whatever is polling our sleep() function that it's time to wake up.

You could probably create your own Future implementation, possibly taking inspiration from the implementation for wasm_bindgen_futures::JsFuture to see how it wires up resolve and reject.

The important bit is line 149 where we take the Wake handle that was stashed away in a previous poll() and call its wake() method.

That is instructive but not the direction I was thinking. The following code does not compile yet, but I am wondering if it can be made to work:

    pub async fn sleep(delay: u32) {
        let cb = |resolve: js_sys::Function, reject: js_sys::Function| {
            web_sys::window()
                .unwrap()
                .set_timeout_with_callback_and_timeout_and_arguments_0(resolve, delay);};

        let p = js_sys::Promise::new(cb);

        p.await}
    pub async fn sleep(delay: i32) {
        let mut cb = |resolve: js_sys::Function, reject: js_sys::Function| {
            web_sys::window()
                .unwrap()
                .set_timeout_with_callback_and_timeout_and_arguments_0(&resolve, delay);};

        let p = js_sys::Promise::new(&mut cb);

        wasm_bindgen_futures::JsFuture::from(p).await.unwrap();}

compiles and appears to work.

I have no idea how safe/unsafe this is to use. Insights anyone? [In particular, I lack the understanding of what preconditions the JS / Rust async 'border' have of each other, so I have no idea if I'm breaking any assumptions here.]

2 Likes

(Caveat: I'm very new to Rust/Wasm.)

I've got code with a dependency on async_std and wasm-bindgen-futures in Cargo.toml that does basically the following, which appears to be working fine:

async fn myfunc() {
...
    let delay = 1000;
    async_std::task::sleep(Duration::from_millis(delay as u64)).await;
...
}
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.