rustwasm / gloo

A modular toolkit for building fast, reliable Web applications and libraries with Rust and WASM
https://gloo-rs.web.app
Apache License 2.0
1.76k stars 145 forks source link

WebExtensions alarms #67

Open OddCoincidence opened 5 years ago

OddCoincidence commented 5 years ago

Summary

Expose an API matching gloo-timers for using alarms in WebExtensions.

Motivation

The MDN documentation describes alarms as "like setTimeout() and setInterval(), except that those functions don't work with background pages that are loaded on demand". Because of these similarities, gloo-timers provides a good precedent for the design of this API.

Detailed Explanation

The gloo-webextensions crate would, like timers, expose both callback-style and futures-based APIs for alarms in submodules called callback and future, the latter gated on the futures feature.

Note: one difference between timers and alarms is that the browser generates ids for timeouts and intervals, but we are responsible for generating a unique name for an alarm.

Callback API skeletons:

pub struct Alarm { .. }

impl Alarm {
    // Create a new alarm that will fire once in `millis` ms
    pub fn new(millis: f64, callback: F) -> Alarm
    where
        F: 'static + FnOnce()
    { .. }

    // returns the generated name for the alarm
    pub fn forget(mut self) -> AlarmId { .. }

    // prevent alarm from firing
    pub fn cancel(mut self) -> Closure<FnOnce()> { .. }
}

// cancels alarm
impl Drop for Alarm { .. }

pub struct RecurringAlarm { .. }

impl RecurringAlarm {
    // create a new alarm that will fire every `minutes` minutes
    pub fn new<F>(minutes: f64, callback: F) -> RecurringAlarm
    where
        F: 'static + FnMut()
    { .. }

    // make the alarm un-cancelable, returning the id
    pub fn forget(&mut self) -> AlarmId { .. }

    // stop the alarm from continuing to fire
    pub fn cancel(mut self) -> Closure<FnMut()> { .. }
}

// cancels alarm
impl Drop for RecurringAlarm { .. }

// newtype wrapper around the generated alarm name
struct AlarmId { .. }

impl AlarmId {
    // cancel the alarm with this id
    pub fn cancel(&self);
}

Futures API skeletons:

pub struct AlarmFuture { .. }

impl AlarmFuture {
    // Create a new alarm future that will become ready in `millis` ms, once polled
    pub fn new(millis: f64, callback: F) -> Alarm
    where
        F: 'static + FnOnce()
    { .. }
}

// cancels alarm
impl Drop for AlarmFuture { .. }

impl Future for AlarmFuture {
    type Item = ();
    type Error = ();

    ..
}

pub struct AlarmStream { .. }

impl AlarmStream {
    // create a new alarm stream that will fire first in `minutes` minutes, and then again every `minutes` minutes, once polled
    pub fn new<F>(minutes: f64, callback: F) -> AlarmStream
    where
        F: 'static + FnMut()
    { .. }

    // create a new alarm stream that will fire first after `delay` minutes, and then again every `minutes` minutes, once polled
    pub fn new_with_delay<F>(minutes: f64, delay: f64, callback: F) -> AlarmStream
    where
        F: 'static + FnMut()
    { .. }
}

// cancels alarm
impl Drop for AlarmStream { .. }

impl Stream for AlarmStream {
    type Item = ();
    type Error = ();

    ..
}

Prior Art

Drawbacks, Rationale, and Alternatives

One alternative would be to design the API to more closely match the raw API, rather than basing this off of gloo-timers.

Unresolved Questions

Is generating a unique alarm name the right thing to do or should that be the user's responsibility as it is for the raw API?

Pauan commented 5 years ago

pub fn new(millis: u32, callback: F) -> Alarm

This needs to accept f64, since Date cannot fit into a u32 (even the current date is far beyond u32).

pub fn forget(mut self) -> String { .. }

I think it would be better if it returned an AlarmId, which would then have a cancel method.

I know that's not what gloo-timers does, but personally I think gloo-timers would benefit from that as well. Hiding implementation details seems like a good idea.

PeriodicAlarm

I think this should be renamed to RecurringAlarm

pub fn new<F>(minutes: u32, callback: F) -> PeriodicAlarm

I'm not sure (I need to test it), but I think it allows for partial minutes, like 1.5, so this should take f64.

Also, sometimes it's useful to specify both when and periodInMinutes, so we should have support for that, possibly through a new method.

OddCoincidence commented 5 years ago

Should we actually use types like std::time::{Instant, Duration} and then convert internally?

I think it would be better if it returned an AlarmId, which would then have a cancel method.

I like this a lot.

I think this should be renamed to RecurringAlarm

:+1:

Also, sometimes it's useful to specify both when and periodInMinutes, so we should have support for that, possibly through a new method.

This got me thinking, should we come up with more descriptive names for the constructors than just new, (combined with above idea) something like:

Alarm::schedule_at(Instant::now().add(Duration::of_secs(10)))

OddCoincidence commented 5 years ago

@Pauan I updated the proposal with your suggestions.