WebExtensions alarms
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
-
gloo-timers -
tokio-timermay also be a good source of inspiration.
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?
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.
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)))
@Pauan I updated the proposal with your suggestions.