perspective_viewer/utils/browser/
request_animation_frame.rs

1// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
3// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
4// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
5// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
6// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
8// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9// ┃ This file is part of the Perspective library, distributed under the terms ┃
10// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
13use ::futures::channel::oneshot::*;
14use perspective_js::utils::{global, *};
15use wasm_bindgen::prelude::*;
16
17/// An `async` version of `queueMicrotask()`, curiously absent from [`web_sys`].
18pub async fn await_queue_microtask() -> ApiResult<()> {
19    #[wasm_bindgen]
20    extern "C" {
21        #[wasm_bindgen(js_name = Window)]
22        type QMWindow;
23
24        #[wasm_bindgen(catch, method, structural, js_class = "Window", js_name = queueMicrotask)]
25        fn queue_microtask(this: &QMWindow, callback: &::js_sys::Function) -> Result<i32, JsValue>;
26    }
27
28    let (sender, receiver) = channel::<()>();
29    let jsfun = Closure::once_into_js(move || sender.send(()).unwrap());
30    js_sys::global()
31        .dyn_into::<QMWindow>()
32        .unwrap()
33        .queue_microtask(jsfun.unchecked_ref())?;
34
35    Ok(receiver.await?)
36}
37
38/// An `async` version of `requestAnimationFrame()`, which resolves on the next
39/// animation frame.
40pub async fn request_animation_frame() {
41    let (sender, receiver) = channel::<()>();
42    let jsfun = Closure::once_into_js(move || sender.send(()).unwrap());
43    global::window()
44        .request_animation_frame(jsfun.unchecked_ref())
45        .unwrap();
46
47    receiver.await.unwrap()
48}
49
50/// An `async` which awaits the browser's `load` event, which is automatically
51/// bypassed if `document.readyState` indicates this has already occurred.
52pub async fn await_dom_loaded() -> ApiResult<()> {
53    let state = global::document().ready_state();
54    if state == "complete" || state == "loaded" {
55        Ok(())
56    } else {
57        let (sender, receiver) = channel::<()>();
58        let jsfun = Closure::once_into_js(move || sender.send(()).unwrap());
59        global::window().add_event_listener_with_callback("load", jsfun.unchecked_ref())?;
60        Ok(receiver.await?)
61    }
62}
63
64/// An `async` version of `set_timeout`, which resolves in `timeout`
65/// milliseconds
66pub async fn set_timeout(timeout: i32) -> ApiResult<()> {
67    let (sender, receiver) = channel::<()>();
68    let jsfun = Closure::once_into_js(move || {
69        let _ = sender.send(());
70    });
71
72    global::window()
73        .set_timeout_with_callback_and_timeout_and_arguments_0(jsfun.unchecked_ref(), timeout)?;
74
75    Ok(receiver.await?)
76}