Working with timers on the Web: `setTimeout` and `setInterval`.
These APIs come in two flavors:
1. a callback style (that more directly mimics the JavaScript APIs), and
2. a `Future`s and `Stream`s API.
### Timeouts
Timeouts fire once after a period of time (measured in milliseconds).
#### Timeouts with a Callback Function
```rust
use gloo_timers::callback::Timeout;
let timeout = Timeout::new(1_000, move || {
// Do something after the one second timeout is up!
});
// Since we don't plan on cancelling the timeout, call `forget`.
timeout.forget();
```
#### Timeouts as `Future`s
With the `futures` feature enabled, a `future` module containing futures-based
timers is exposed.
gloo-timers-0.3.0/src/callback.rs 0000644 0000000 0000000 00000014360 10461020230 0014762 0 ustar 0000000 0000000 //! 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;
#[wasm_bindgen(js_name = "setInterval", catch)]
fn set_interval(handler: &Function, timeout: i32) -> Result;
#[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,
closure: Option>,
}
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(millis: u32, callback: F) -> Timeout
where
F: 'static + FnOnce(),
{
let closure = Closure::once(callback);
let id = set_timeout(
closure.as_ref().unchecked_ref::(),
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 {
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,
closure: Option>,
}
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(millis: u32, callback: F) -> Interval
where
F: 'static + FnMut(),
{
let closure = Closure::wrap(Box::new(callback) as Box);
let id = set_interval(
closure.as_ref().unchecked_ref::(),
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 {
self.closure.take().unwrap_throw()
}
}
gloo-timers-0.3.0/src/future.rs 0000644 0000000 0000000 00000011213 10461020230 0014532 0 ustar 0000000 0000000 //! `Future`- and `Stream`-backed timers APIs.
use crate::callback::{Interval, Timeout};
use futures_channel::{mpsc, oneshot};
use futures_core::stream::Stream;
use std::convert::TryFrom;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
use wasm_bindgen::prelude::*;
/// A scheduled timeout as a `Future`.
///
/// See `TimeoutFuture::new` for scheduling new timeouts.
///
/// Once scheduled, if you change your mind and don't want the timeout to fire,
/// you can `drop` the future.
///
/// A timeout future will never resolve to `Err`. Its only failure mode is when
/// the timeout is so long that it is effectively infinite and never fires.
///
/// # Example
///
/// ```no_run
/// use gloo_timers::future::TimeoutFuture;
/// use futures_util::future::{select, Either};
/// use wasm_bindgen_futures::spawn_local;
///
/// spawn_local(async {
/// match select(TimeoutFuture::new(1_000), TimeoutFuture::new(2_000)).await {
/// Either::Left((val, b)) => {
/// // Drop the `2_000` ms timeout to cancel its timeout.
/// drop(b);
/// }
/// Either::Right((a, val)) => {
/// panic!("the `1_000` ms timeout should have won this race");
/// }
/// }
/// });
/// ```
#[derive(Debug)]
#[must_use = "futures do nothing unless polled or spawned"]
pub struct TimeoutFuture {
_inner: Timeout,
rx: oneshot::Receiver<()>,
}
impl TimeoutFuture {
/// Create a new timeout future.
///
/// Remember that futures do nothing unless polled or spawned, so either
/// pass this future to `wasm_bindgen_futures::spawn_local` or use it inside
/// another future.
///
/// # Example
///
/// ```no_run
/// use gloo_timers::future::TimeoutFuture;
/// use wasm_bindgen_futures::spawn_local;
///
/// spawn_local(async {
/// TimeoutFuture::new(1_000).await;
/// // Do stuff after one second...
/// });
/// ```
pub fn new(millis: u32) -> TimeoutFuture {
let (tx, rx) = oneshot::channel();
let inner = Timeout::new(millis, move || {
// if the receiver was dropped we do nothing.
tx.send(()).unwrap_throw();
});
TimeoutFuture { _inner: inner, rx }
}
}
/// Waits until the specified duration has elapsed.
///
/// # Panics
///
/// This function will panic if the specified [`Duration`] cannot be casted into a u32 in
/// milliseconds.
///
/// # Example
///
/// ```compile_fail
/// use std::time::Duration;
/// use gloo_timers::future::sleep;
///
/// sleep(Duration::from_secs(1)).await;
/// ```
pub fn sleep(dur: Duration) -> TimeoutFuture {
let millis = u32::try_from(dur.as_millis())
.expect_throw("failed to cast the duration into a u32 with Duration::as_millis.");
TimeoutFuture::new(millis)
}
impl Future for TimeoutFuture {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll {
Future::poll(Pin::new(&mut self.rx), cx).map(|t| t.unwrap_throw())
}
}
/// A scheduled interval as a `Stream`.
///
/// See `IntervalStream::new` for scheduling new intervals.
///
/// Once scheduled, if you want to stop the interval from continuing to fire,
/// you can `drop` the stream.
///
/// An interval stream will never resolve to `Err`.
#[derive(Debug)]
#[must_use = "streams do nothing unless polled or spawned"]
pub struct IntervalStream {
receiver: mpsc::UnboundedReceiver<()>,
_inner: Interval,
}
impl IntervalStream {
/// Create a new interval stream.
///
/// Remember that streams do nothing unless polled or spawned, so either
/// spawn this stream via `wasm_bindgen_futures::spawn_local` or use it inside
/// another stream or future.
///
/// # Example
///
/// ```compile_fail
/// use futures_util::stream::StreamExt;
/// use gloo_timers::future::IntervalStream;
/// use wasm_bindgen_futures::spawn_local;
///
/// spawn_local(async {
/// IntervalStream::new(1_000).for_each(|_| {
/// // Do stuff every one second...
/// }).await;
/// });
/// ```
pub fn new(millis: u32) -> IntervalStream {
let (sender, receiver) = mpsc::unbounded();
let inner = Interval::new(millis, move || {
// if the receiver was dropped we do nothing.
sender.unbounded_send(()).unwrap_throw();
});
IntervalStream {
receiver,
_inner: inner,
}
}
}
impl Stream for IntervalStream {
type Item = ();
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll