async-foundation 0.2.1

Foundational async primitives for Rust - timers, networking, and common utilities
Documentation
use futures::FutureExt;
use futures::future::FusedFuture;
use js_sys::{Function, Promise};
use wasm_bindgen::prelude::Closure;
use wasm_bindgen::{JsCast, JsValue, prelude::wasm_bindgen};
use wasm_bindgen_futures::JsFuture;

struct TimerResource(Option<(Closure<dyn FnMut()>, usize)>);

impl Drop for TimerResource {
    fn drop(&mut self) {
        if let Some((_, timeout_id)) = self.0 {
            clear_timeout(timeout_id);
        }
    }
}

pub fn wait(milliseconds: usize) -> impl FusedFuture<Output = Result<JsValue, JsValue>> {
    async move {
        let mut resource = TimerResource(None);
        let promise = Promise::new(&mut |resolve: Function, _reject: Function| {
            let cb = get_timout_callback(&resolve);
            let timeout_id = set_timeout(cb.as_ref().unchecked_ref(), milliseconds);
            resource.0.replace((cb, timeout_id));
        });

        JsFuture::from(promise).await
    }
    .fuse()
}

fn get_timout_callback(resolve: &Function) -> Closure<dyn FnMut()> {
    let resolve = resolve.clone();
    let cb = move || {
        resolve
            .call0(&JsValue::undefined())
            .expect("cannot resolve callback");
    };
    Closure::once(cb)
}

#[wasm_bindgen]
extern "C" {
    // Import JavaScript's setTimeout function
    #[wasm_bindgen(js_name = setTimeout)]
    fn set_timeout(closure: &js_sys::Function, millis: usize) -> usize;
    #[wasm_bindgen(js_name = clearTimeout)]
    fn clear_timeout(timeout_id: usize);
}

// #[cfg(test)]
// mod test {
//     use super::*;
//     use futures::executor::block_on;
//     use wasm_bindgen_test::wasm_bindgen_test;

//     //works in real time but not with wasm-bindgen-test-runner and "cargo test --target wasm32-unknown-unknown" command
//     #[wasm_bindgen_test]
//     fn test_timout_wait_wasm32(){
//         let f = wait(10);
//         block_on(f).unwrap();
//     }

// }