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" {
#[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);
}