1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
//! Callback-style timer APIs.
use js_sys::Function;
use wasm_bindgen::prelude::*;
use wasm_bindgen::{JsCast, JsValue};
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_name = "setTimeout", catch)]
fn set_timeout(handler: &Function, timeout: i32) -> Result<JsValue, JsValue>;
#[wasm_bindgen(js_name = "setInterval", catch)]
fn set_interval(handler: &Function, timeout: i32) -> Result<JsValue, JsValue>;
#[wasm_bindgen(js_name = "clearTimeout")]
fn clear_timeout(handle: JsValue) -> JsValue;
#[wasm_bindgen(js_name = "clearInterval")]
fn clear_interval(handle: JsValue) -> JsValue;
}
/// A scheduled timeout.
///
/// See `Timeout::new` for scheduling new timeouts.
///
/// Once scheduled, you can [`drop`] the [`Timeout`] to clear it or [`forget`](Timeout::forget) to leak it. Once forgotten, the interval will keep running forever.
/// This pattern is known as Resource Acquisition Is Initialization (RAII).
#[derive(Debug)]
#[must_use = "timeouts cancel on drop; either call `forget` or `drop` explicitly"]
pub struct Timeout {
id: Option<JsValue>,
closure: Option<Closure<dyn FnMut()>>,
}
impl Drop for Timeout {
/// Disposes of the timeout, dually cancelling this timeout by calling
/// `clearTimeout` directly.
fn drop(&mut self) {
if let Some(id) = self.id.take() {
clear_timeout(id);
}
}
}
impl Timeout {
/// Schedule a timeout to invoke `callback` in `millis` milliseconds from
/// now.
///
/// # Example
///
/// ```no_run
/// use gloo_timers::callback::Timeout;
///
/// let timeout = Timeout::new(1_000, move || {
/// // Do something...
/// });
/// ```
pub fn new<F>(millis: u32, callback: F) -> Timeout
where
F: 'static + FnOnce(),
{
let closure = Closure::once(callback);
let id = set_timeout(
closure.as_ref().unchecked_ref::<js_sys::Function>(),
millis as i32,
)
.unwrap_throw();
Timeout {
id: Some(id),
closure: Some(closure),
}
}
/// Forgets this resource without clearing the timeout.
///
/// Returns the identifier returned by the original `setTimeout` call, and
/// therefore you can still cancel the timeout by calling `clearTimeout`
/// directly (perhaps via `web_sys::clear_timeout_with_handle`).
///
/// # Example
///
/// ```no_run
/// use gloo_timers::callback::Timeout;
///
/// // We definitely want to do stuff, and aren't going to ever cancel this
/// // timeout.
/// Timeout::new(1_000, || {
/// // Do stuff...
/// }).forget();
/// ```
pub fn forget(mut self) -> JsValue {
let id = self.id.take().unwrap_throw();
self.closure.take().unwrap_throw().forget();
id
}
/// Cancel this timeout so that the callback is not invoked after the time
/// is up.
///
/// The scheduled callback is returned.
///
/// # Example
///
/// ```no_run
/// use gloo_timers::callback::Timeout;
///
/// let timeout = Timeout::new(1_000, || {
/// // Do stuff...
/// });
///
/// // If actually we didn't want to set a timer, then cancel it.
/// if nevermind() {
/// timeout.cancel();
/// }
/// # fn nevermind() -> bool { true }
/// ```
pub fn cancel(mut self) -> Closure<dyn FnMut()> {
self.closure.take().unwrap_throw()
}
}
/// A scheduled interval.
///
/// See `Interval::new` for scheduling new intervals.
///
/// Once scheduled, you can [`drop`] the [`Interval`] to clear it or [`forget`](Interval::forget) to leak it. Once forgotten, the interval will keep running forever.
/// This pattern is known as Resource Acquisition Is Initialization (RAII).
#[derive(Debug)]
#[must_use = "intervals cancel on drop; either call `forget` or `drop` explicitly"]
pub struct Interval {
id: Option<JsValue>,
closure: Option<Closure<dyn FnMut()>>,
}
impl Drop for Interval {
/// Disposes of the interval, dually cancelling this interval by calling
/// `clearInterval` directly.
fn drop(&mut self) {
if let Some(id) = self.id.take() {
clear_interval(id);
}
}
}
impl Interval {
/// Schedule an interval to invoke `callback` every `millis` milliseconds.
///
/// # Example
///
/// ```no_run
/// use gloo_timers::callback::Interval;
///
/// let interval = Interval::new(1_000, move || {
/// // Do something...
/// });
/// ```
pub fn new<F>(millis: u32, callback: F) -> Interval
where
F: 'static + FnMut(),
{
let closure = Closure::wrap(Box::new(callback) as Box<dyn FnMut()>);
let id = set_interval(
closure.as_ref().unchecked_ref::<js_sys::Function>(),
millis as i32,
)
.unwrap_throw();
Interval {
id: Some(id),
closure: Some(closure),
}
}
/// Forget this resource without clearing the interval.
///
/// Returns the identifier returned by the original `setInterval` call, and
/// therefore you can still cancel the interval by calling `clearInterval`
/// directly (perhaps via `web_sys::clear_interval_with_handle`).
///
/// # Example
///
/// ```no_run
/// use gloo_timers::callback::Interval;
///
/// // We want to do stuff every second, indefinitely.
/// Interval::new(1_000, || {
/// // Do stuff...
/// }).forget();
/// ```
pub fn forget(mut self) -> JsValue {
let id = self.id.take().unwrap_throw();
self.closure.take().unwrap_throw().forget();
id
}
/// Cancel this interval so that the callback is no longer periodically
/// invoked.
///
/// The scheduled callback is returned.
///
/// # Example
///
/// ```no_run
/// use gloo_timers::callback::Interval;
///
/// let interval = Interval::new(1_000, || {
/// // Do stuff...
/// });
///
/// // If we don't want this interval to run anymore, then cancel it.
/// if nevermind() {
/// interval.cancel();
/// }
/// # fn nevermind() -> bool { true }
/// ```
pub fn cancel(mut self) -> Closure<dyn FnMut()> {
self.closure.take().unwrap_throw()
}
}