Rename async_repeat into task (#528)

Following [the discussion in
Zulip](https://xi.zulipchat.com/#narrow/stream/354396-xilem/topic/.60async_repeat.60.20name.20brainstorming),
this PR renames `async_repeat` view into `task`, and its `future_future`
argument into `init_future`. I also assumed we would want to do a
corresponding rename in `xilem_web` for consistency.
This commit is contained in:
Andrii Zymohliad 2024-08-20 11:50:47 +03:00 committed by GitHub
parent 42a6a320da
commit f90772a0d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 55 additions and 70 deletions

View File

@ -9,7 +9,7 @@ use std::time::Duration;
use xilem::{
tokio::time,
view::{
async_repeat, button, button_any_pointer, checkbox, flex, label, prose, textbox, Axis,
button, button_any_pointer, checkbox, flex, label, prose, task, textbox, Axis,
FlexExt as _, FlexSpacer,
},
Color, EventLoop, EventLoopBuilder, TextAlignment, WidgetView, Xilem,
@ -92,10 +92,10 @@ fn app_logic(data: &mut AppData) -> impl WidgetView<AppData> {
button("Reset", |data: &mut AppData| data.count = 0),
flex((fizz_buzz_flex_sequence, flex_sequence)).direction(axis),
)),
// The following `async_repeat` view only exists whilst the example is in the "active" state, so
// The following `task` view only exists whilst the example is in the "active" state, so
// the updates it performs will only be running whilst we are in that state.
data.active.then(|| {
async_repeat(
task(
|proxy| async move {
let mut interval = time::interval(Duration::from_secs(1));
loop {

View File

@ -12,7 +12,7 @@ use tokio::time;
use tracing::warn;
use winit::error::EventLoopError;
use winit::window::Window;
use xilem::view::{async_repeat, button, flex, label, FlexSequence, FlexSpacer};
use xilem::view::{button, flex, label, task, FlexSequence, FlexSpacer};
use xilem::{WidgetView, Xilem};
use xilem_core::fork;
use xilem_core::one_of::Either;
@ -117,7 +117,7 @@ fn app_logic(data: &mut Stopwatch) -> impl WidgetView<Stopwatch> {
)),
data.active.then(|| {
// Only update while active.
async_repeat(
task(
|proxy| async move {
let mut interval = time::interval(Duration::from_millis(50));
loop {

View File

@ -13,8 +13,7 @@ use time::{error::IndeterminateOffset, macros::format_description, OffsetDateTim
use winit::error::EventLoopError;
use xilem::{
view::{
async_repeat, button, flex, label, prose, sized_box, variable_label, Axis, FlexExt,
FlexSpacer,
button, flex, label, prose, sized_box, task, variable_label, Axis, FlexExt, FlexSpacer,
},
Color, EventLoop, EventLoopBuilder, WidgetView, Xilem,
};
@ -49,7 +48,7 @@ fn app_logic(data: &mut Clocks) -> impl WidgetView<Clocks> {
));
fork(
view,
async_repeat(
task(
|proxy| async move {
// TODO: Synchronise with the actual "second" interval. This is expected to show the wrong second
// ~50% of the time.

View File

@ -1,8 +1,8 @@
// Copyright 2024 the Xilem Authors
// SPDX-License-Identifier: Apache-2.0
mod async_repeat;
pub use async_repeat::*;
mod task;
pub use task::*;
mod button;
pub use button::*;

View File

@ -11,20 +11,17 @@ use xilem_core::{
use crate::ViewCtx;
/// Launch a task which will run until the view is no longer in the tree.
/// `future_future` is given a [`MessageProxy`], which it will store in the future it returns.
/// `init_future` is given a [`MessageProxy`], which it will store in the future it returns.
/// This `MessageProxy` can be used to send a message to `on_event`, which can then update
/// the app's state.
///
/// For exampe, this can be used with the time functions in [`crate::tokio::time`].
///
/// Note that this task will not be updated if the view is rebuilt, so `future_future`
/// Note that this task will not be updated if the view is rebuilt, so `init_future`
/// cannot capture.
// TODO: More thorough documentation.
/// See [`run_once`](crate::core::run_once) for details.
pub fn async_repeat<M, F, H, State, Action, Fut>(
future_future: F,
on_event: H,
) -> AsyncRepeat<F, H, M>
pub fn task<M, F, H, State, Action, Fut>(init_future: F, on_event: H) -> Task<F, H, M>
where
F: Fn(MessageProxy<M>) -> Fut,
Fut: Future<Output = ()> + Send + 'static,
@ -34,12 +31,12 @@ where
const {
assert!(
core::mem::size_of::<F>() == 0,
"`async_repeat` will not be ran again when its captured variables are updated.\n\
To ignore this warning, use `async_repeat_raw`."
"`task` will not be ran again when its captured variables are updated.\n\
To ignore this warning, use `task_raw`."
);
};
AsyncRepeat {
future_future,
Task {
init_future,
on_event,
message: PhantomData,
}
@ -47,33 +44,30 @@ where
/// Launch a task which will run until the view is no longer in the tree.
///
/// This is [`async_repeat`] without the capturing rules.
/// See `async_repeat` for full documentation.
pub fn async_repeat_raw<M, F, H, State, Action, Fut>(
future_future: F,
on_event: H,
) -> AsyncRepeat<F, H, M>
/// This is [`task`] without the capturing rules.
/// See `task` for full documentation.
pub fn task_raw<M, F, H, State, Action, Fut>(init_future: F, on_event: H) -> Task<F, H, M>
where
F: Fn(MessageProxy<M>) -> Fut,
Fut: Future<Output = ()> + Send + 'static,
H: Fn(&mut State, M) -> Action + 'static,
M: Message + 'static,
{
AsyncRepeat {
future_future,
Task {
init_future,
on_event,
message: PhantomData,
}
}
pub struct AsyncRepeat<F, H, M> {
future_future: F,
pub struct Task<F, H, M> {
init_future: F,
on_event: H,
message: PhantomData<fn() -> M>,
}
impl<F, H, M> ViewMarker for AsyncRepeat<F, H, M> {}
impl<State, Action, F, H, M, Fut> View<State, Action, ViewCtx> for AsyncRepeat<F, H, M>
impl<F, H, M> ViewMarker for Task<F, H, M> {}
impl<State, Action, F, H, M, Fut> View<State, Action, ViewCtx> for Task<F, H, M>
where
F: Fn(MessageProxy<M>) -> Fut + 'static,
Fut: Future<Output = ()> + Send + 'static,
@ -90,7 +84,7 @@ where
let proxy = ctx.proxy.clone();
let handle = ctx
.runtime()
.spawn((self.future_future)(MessageProxy::new(proxy, path)));
.spawn((self.init_future)(MessageProxy::new(proxy, path)));
(NoElement, handle)
}
@ -122,7 +116,7 @@ where
) -> xilem_core::MessageResult<Action> {
debug_assert!(
id_path.is_empty(),
"id path should be empty in AsyncRepeat::message"
"id path should be empty in Task::message"
);
let message = message.downcast::<M>().unwrap();
xilem_core::MessageResult::Action((self.on_event)(app_state, *message))

View File

@ -3,10 +3,8 @@
//! Async views, allowing concurrent operations, like fetching data from a server
mod async_repeat;
pub use async_repeat::{
async_repeat, async_repeat_raw, AsyncRepeat, AsyncRepeatProxy, ShutdownSignal,
};
mod task;
pub use task::{task, task_raw, ShutdownSignal, Task, TaskProxy};
mod interval;
pub use interval::{interval, Interval};

View File

@ -12,20 +12,17 @@ use crate::{context::MessageThunk, DynMessage, Message, ViewCtx};
/// Spawn an async task to update state asynchronously
///
/// The `init_future` function is given an [`AsyncRepeatProxy`] and a [`ShutdownSignal`].
/// The `AsyncRepeatProxy` can be used to send a message to `on_event`, which can then update
/// The `init_future` function is given a [`TaskProxy`] and a [`ShutdownSignal`].
/// The `TaskProxy` can be used to send a message to `on_event`, which can then update
/// the app's state.
/// The `ShutdownSignal` can be used to detect whether the view has disappeared and
/// a shutdown request has been made (because a future cannot be canceled from the outside).
///
/// Note that this task will not be updated if the view is rebuilt, so `future`
/// Note that this task will not be updated if the view is rebuilt, so `init_future`
/// cannot capture.
pub fn async_repeat<M, F, H, State, Action, Fut>(
init_future: F,
on_event: H,
) -> AsyncRepeat<F, H, M>
pub fn task<M, F, H, State, Action, Fut>(init_future: F, on_event: H) -> Task<F, H, M>
where
F: Fn(AsyncRepeatProxy, ShutdownSignal) -> Fut + 'static,
F: Fn(TaskProxy, ShutdownSignal) -> Fut + 'static,
Fut: Future<Output = ()> + 'static,
H: Fn(&mut State, M) -> Action + 'static,
M: Message,
@ -33,11 +30,11 @@ where
const {
assert!(
core::mem::size_of::<F>() == 0,
"`async_repeat` will not be ran again when its captured variables are updated.\n\
To ignore this warning, use `async_repeat_raw`."
"`task` will not be ran again when its captured variables are updated.\n\
To ignore this warning, use `task_raw`."
);
};
AsyncRepeat {
Task {
init_future,
on_event,
message: PhantomData,
@ -46,18 +43,15 @@ where
/// Spawn a future.
///
/// This is [`async_repeat`] without the capturing rules.
/// See `async_repeat` for full documentation.
pub fn async_repeat_raw<M, F, H, State, Action, Fut>(
init_future: F,
on_event: H,
) -> AsyncRepeat<F, H, M>
/// This is [`task`] without the capturing rules.
/// See `task` for full documentation.
pub fn task_raw<M, F, H, State, Action, Fut>(init_future: F, on_event: H) -> Task<F, H, M>
where
F: Fn(AsyncRepeatProxy, ShutdownSignal) -> Fut + 'static,
F: Fn(TaskProxy, ShutdownSignal) -> Fut + 'static,
Fut: Future<Output = ()> + 'static,
H: Fn(&mut State, M) -> Action + 'static,
{
AsyncRepeat {
Task {
init_future,
on_event,
message: PhantomData,
@ -100,21 +94,21 @@ impl ShutdownSignal {
}
}
pub struct AsyncRepeat<F, H, M> {
pub struct Task<F, H, M> {
init_future: F,
on_event: H,
message: PhantomData<fn() -> M>,
}
pub struct AsyncRepeatState {
pub struct TaskState {
abort_handle: Option<AbortHandle>,
}
pub struct AsyncRepeatProxy {
pub struct TaskProxy {
thunk: Rc<MessageThunk>,
}
impl AsyncRepeatProxy {
impl TaskProxy {
pub fn send_message<M>(&self, message: M)
where
M: Message,
@ -126,28 +120,28 @@ impl AsyncRepeatProxy {
}
}
impl<F, H, M> ViewMarker for AsyncRepeat<F, H, M> {}
impl<F, H, M> ViewMarker for Task<F, H, M> {}
impl<State, Action, F, H, M, Fut> View<State, Action, ViewCtx, DynMessage> for AsyncRepeat<F, H, M>
impl<State, Action, F, H, M, Fut> View<State, Action, ViewCtx, DynMessage> for Task<F, H, M>
where
State: 'static,
Action: 'static,
F: Fn(AsyncRepeatProxy, ShutdownSignal) -> Fut + 'static,
F: Fn(TaskProxy, ShutdownSignal) -> Fut + 'static,
Fut: Future<Output = ()> + 'static,
H: Fn(&mut State, M) -> Action + 'static,
M: Message,
{
type Element = NoElement;
type ViewState = AsyncRepeatState;
type ViewState = TaskState;
fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
let thunk = ctx.message_thunk();
let (shutdown_signal, abort_handle) = ShutdownSignal::new();
let view_state = AsyncRepeatState {
let view_state = TaskState {
abort_handle: Some(abort_handle),
};
let proxy = AsyncRepeatProxy {
let proxy = TaskProxy {
thunk: Rc::new(thunk),
};
spawn_local((self.init_future)(proxy, shutdown_signal));

View File

@ -7,7 +7,7 @@
use futures::{select, FutureExt};
use gloo_timers::future::TimeoutFuture;
use xilem_web::{
concurrent::{async_repeat, AsyncRepeatProxy, ShutdownSignal},
concurrent::{task, ShutdownSignal, TaskProxy},
core::fork,
core::one_of::Either,
document_body,
@ -31,7 +31,7 @@ enum Message {
/// This is just to simulate some async behavior.
/// If you just need an interval, you should use
/// [`interval`](xilem_web::concurrent::interval) instead.
async fn create_ping_task(proxy: AsyncRepeatProxy, shutdown_signal: ShutdownSignal) {
async fn create_ping_task(proxy: TaskProxy, shutdown_signal: ShutdownSignal) {
log::debug!("Start ping task");
let mut abort = shutdown_signal.into_future().fuse();
@ -55,7 +55,7 @@ async fn create_ping_task(proxy: AsyncRepeatProxy, shutdown_signal: ShutdownSign
}
fn app_logic(state: &mut AppState) -> impl Element<AppState> {
let task = async_repeat(
let task = task(
create_ping_task,
|state: &mut AppState, message: Message| match message {
Message::Ping => {