Skip to content

Commit

Permalink
Rename MemoizedInner -> MemoizedFuture
Browse files Browse the repository at this point in the history
  • Loading branch information
flosse committed Nov 26, 2024
1 parent a28aaf3 commit 4aed1f3
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 64 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

130 changes: 74 additions & 56 deletions xilem_web/src/concurrent/memoized_await.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// Copyright 2024 the Xilem Authors and the Druid Authors
// SPDX-License-Identifier: Apache-2.0

use std::{fmt, future::Future, marker::PhantomData};
use std::{fmt, future::Future, marker::PhantomData, rc::Rc};

use futures::{Stream, StreamExt};
use wasm_bindgen::{closure::Closure, JsCast, UnwrapThrowExt};
use wasm_bindgen_futures::spawn_local;

use crate::{
core::{MessageResult, Mut, NoElement, View, ViewId, ViewMarker, ViewPathTracker},
DynMessage, OptionalAction, ViewCtx,
DynMessage, MessageThunk, OptionalAction, ViewCtx,
};

/// Await a future returned by `init_future` invoked with the argument `data`, `callback` is called with the output of the future.
Expand Down Expand Up @@ -38,7 +38,7 @@ impl<State, Action, OA, InitFuture, Data, Callback, F, FOut>
where
FOut: fmt::Debug + 'static,
F: Future<Output = FOut> + 'static,
InitFuture: Fn(&Data) -> F,
InitFuture: Fn(&mut State, &Data) -> F,
{
/// Debounce the `init_future` function, when `data` updates,
/// when `reset_debounce_on_update == false` then this throttles updates each `milliseconds`
Expand All @@ -64,7 +64,7 @@ impl<State, Action, OA, InitStream, Data, Callback, F, StreamItem>
where
StreamItem: fmt::Debug + 'static,
F: Stream<Item = StreamItem> + 'static,
InitStream: Fn(&Data) -> F,
InitStream: Fn(&mut State, &Data) -> F,
{
/// Debounce the `init_stream` function, when `data` updates,
/// when `reset_debounce_on_update == false` then this throttles updates each `milliseconds`
Expand All @@ -87,39 +87,33 @@ where

fn init_future<State, Action, OA, InitFuture, Data, Callback, F, FOut>(
m: &MemoizedFuture<State, Action, OA, InitFuture, Data, Callback, F, FOut>,
ctx: &mut ViewCtx,
generation: u64,
thunk: Rc<MessageThunk>,
state: &mut State,
) where
InitFuture: Fn(&Data) -> F + 'static,
InitFuture: Fn(&mut State, &Data) -> F + 'static,
FOut: fmt::Debug + 'static,
F: Future<Output = FOut> + 'static,
{
ctx.with_id(ViewId::new(generation), |ctx| {
let thunk = ctx.message_thunk();
let future = (m.init_future)(&m.data);
spawn_local(async move {
thunk.push_message(MemoizedFutureMessage::<FOut>::Output(future.await));
});
let future = (m.init_future)(state, &m.data);
spawn_local(async move {
thunk.push_message(MemoizedFutureMessage::<FOut>::Output(future.await));
});
}

fn init_stream<State, Action, OA, InitStream, Data, Callback, F, StreamItem>(
m: &MemoizedFuture<State, Action, OA, InitStream, Data, Callback, F, StreamItem>,
ctx: &mut ViewCtx,
generation: u64,
thunk: Rc<MessageThunk>,
state: &mut State,
) where
InitStream: Fn(&Data) -> F + 'static,
InitStream: Fn(&mut State, &Data) -> F + 'static,
StreamItem: fmt::Debug + 'static,
F: Stream<Item = StreamItem> + 'static,
{
ctx.with_id(ViewId::new(generation), |ctx| {
let thunk = ctx.message_thunk();
let mut stream = Box::pin((m.init_future)(&m.data));
spawn_local(async move {
while let Some(item) = stream.next().await {
thunk.push_message(MemoizedFutureMessage::<StreamItem>::Output(item));
}
});
let mut stream = Box::pin((m.init_future)(state, &m.data));
spawn_local(async move {
while let Some(item) = stream.next().await {
thunk.push_message(MemoizedFutureMessage::<StreamItem>::Output(item));
}
});
}

Expand Down Expand Up @@ -154,7 +148,7 @@ where
Data: PartialEq + 'static,
FOut: fmt::Debug + 'static,
F: Future<Output = FOut> + 'static,
InitFuture: Fn(&Data) -> F + 'static,
InitFuture: Fn(&mut State, &Data) -> F + 'static,
OA: OptionalAction<Action> + 'static,
Callback: Fn(&mut State, FOut) -> OA + 'static,
{
Expand Down Expand Up @@ -212,7 +206,7 @@ where
Data: PartialEq + 'static,
StreamItem: fmt::Debug + 'static,
F: Stream<Item = StreamItem> + 'static,
InitStream: Fn(&Data) -> F + 'static,
InitStream: Fn(&mut State, &Data) -> F + 'static,
OA: OptionalAction<Action> + 'static,
Callback: Fn(&mut State, StreamItem) -> OA + 'static,
{
Expand All @@ -226,7 +220,6 @@ where
})
}

#[derive(Default)]
#[allow(unnameable_types)] // reason: Implementation detail, public because of trait visibility rules
pub struct MemoizedAwaitState {
generation: u64,
Expand All @@ -235,9 +228,20 @@ pub struct MemoizedAwaitState {
schedule_update_fn: Option<Closure<dyn FnMut()>>,
schedule_update_timeout_handle: Option<i32>,
update: bool,
thunk: Rc<MessageThunk>,
}

impl MemoizedAwaitState {
fn new(thunk: MessageThunk) -> Self {
Self {
generation: 0,
schedule_update: false,
schedule_update_fn: None,
schedule_update_timeout_handle: None,
update: false,
thunk: Rc::new(thunk),
}
}
fn clear_update_timeout(&mut self) {
if let Some(handle) = self.schedule_update_timeout_handle {
web_sys::window()
Expand All @@ -248,11 +252,13 @@ impl MemoizedAwaitState {
self.schedule_update_fn = None;
}

fn reset_debounce_timeout_and_schedule_update<FOut: fmt::Debug + 'static>(
fn reset_debounce_timeout_and_schedule_update<FOut>(
&mut self,
ctx: &mut ViewCtx,
debounce_duration: usize,
) {
) where
FOut: fmt::Debug + 'static,
{
ctx.with_id(ViewId::new(self.generation), |ctx| {
self.clear_update_timeout();
let thunk = ctx.message_thunk();
Expand All @@ -271,12 +277,24 @@ impl MemoizedAwaitState {
self.schedule_update = true;
});
}

fn request_init<FOut>(&mut self, ctx: &mut ViewCtx)
where
FOut: fmt::Debug + 'static,
{
ctx.with_id(ViewId::new(self.generation), |ctx| {
self.thunk = Rc::new(ctx.message_thunk());
self.thunk
.enqueue_message(MemoizedFutureMessage::<FOut>::RequestInit);
});
}
}

#[derive(Debug)]
enum MemoizedFutureMessage<Output: fmt::Debug> {
Output(Output),
ScheduleUpdate,
RequestInit,
}

impl<State, Action, OA, InitFuture, Data, CB, F, FOut> ViewMarker
Expand All @@ -294,7 +312,7 @@ where
State: 'static,
Action: 'static,
OA: OptionalAction<Action> + 'static,
InitFuture: Fn(&Data) -> F + 'static,
InitFuture: Fn(&mut State, &Data) -> F + 'static,
FOut: fmt::Debug + 'static,
Data: PartialEq + 'static,
F: Future<Output = FOut> + 'static,
Expand All @@ -305,7 +323,7 @@ where
type ViewState = MemoizedAwaitState;

fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
self.0.build(ctx, init_future)
self.0.build(ctx)
}

fn rebuild(
Expand All @@ -315,7 +333,7 @@ where
ctx: &mut ViewCtx,
(): Mut<Self::Element>,
) {
self.0.rebuild(&prev.0, view_state, ctx, init_future);
self.0.rebuild(&prev.0, view_state, ctx);
}

fn teardown(&self, state: &mut Self::ViewState, _: &mut ViewCtx, (): Mut<Self::Element>) {
Expand All @@ -329,7 +347,8 @@ where
message: DynMessage,
app_state: &mut State,
) -> MessageResult<Action, DynMessage> {
self.0.message(view_state, id_path, message, app_state)
self.0
.message(view_state, id_path, message, app_state, init_future)
}
}

Expand All @@ -340,7 +359,7 @@ where
State: 'static,
Action: 'static,
OA: OptionalAction<Action> + 'static,
InitStream: Fn(&Data) -> F + 'static,
InitStream: Fn(&mut State, &Data) -> F + 'static,
StreamItem: fmt::Debug + 'static,
Data: PartialEq + 'static,
F: Stream<Item = StreamItem> + 'static,
Expand All @@ -351,7 +370,7 @@ where
type ViewState = MemoizedAwaitState;

fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
self.0.build(ctx, init_stream)
self.0.build(ctx)
}

fn rebuild(
Expand All @@ -361,7 +380,7 @@ where
ctx: &mut ViewCtx,
(): Mut<Self::Element>,
) {
self.0.rebuild(&prev.0, view_state, ctx, init_stream);
self.0.rebuild(&prev.0, view_state, ctx);
}

fn teardown(&self, state: &mut Self::ViewState, _: &mut ViewCtx, (): Mut<Self::Element>) {
Expand All @@ -375,7 +394,8 @@ where
message: DynMessage,
app_state: &mut State,
) -> MessageResult<Action, DynMessage> {
self.0.message(view_state, id_path, message, app_state)
self.0
.message(view_state, id_path, message, app_state, init_stream)
}
}

Expand All @@ -385,36 +405,26 @@ where
State: 'static,
Action: 'static,
OA: OptionalAction<Action> + 'static,
InitFuture: Fn(&Data) -> F + 'static,
InitFuture: Fn(&mut State, &Data) -> F + 'static,
FOut: fmt::Debug + 'static,
Data: PartialEq + 'static,
F: 'static,
CB: Fn(&mut State, FOut) -> OA + 'static,
{
fn build<I>(&self, ctx: &mut ViewCtx, init_future: I) -> (NoElement, MemoizedAwaitState)
where
I: Fn(&Self, &mut ViewCtx, u64),
{
let mut state = MemoizedAwaitState::default();
fn build(&self, ctx: &mut ViewCtx) -> (NoElement, MemoizedAwaitState) {
let thunk = ctx.message_thunk();
let mut state = MemoizedAwaitState::new(thunk);

if self.debounce_ms > 0 {
state.reset_debounce_timeout_and_schedule_update::<FOut>(ctx, self.debounce_ms);
} else {
init_future(self, ctx, state.generation);
state.request_init::<FOut>(ctx);
}

(NoElement, state)
}

fn rebuild<I>(
&self,
prev: &Self,
view_state: &mut MemoizedAwaitState,
ctx: &mut ViewCtx,
init_future: I,
) where
I: Fn(&Self, &mut ViewCtx, u64),
{
fn rebuild(&self, prev: &Self, view_state: &mut MemoizedAwaitState, ctx: &mut ViewCtx) {
let debounce_has_changed_and_update_is_scheduled = view_state.schedule_update
&& (prev.reset_debounce_on_update != self.reset_debounce_on_update
|| prev.debounce_ms != self.debounce_ms);
Expand Down Expand Up @@ -444,7 +454,7 @@ where
// no debounce
view_state.generation += 1;
view_state.update = false;
init_future(self, ctx, view_state.generation);
view_state.request_init::<FOut>(ctx);
}
}
}
Expand All @@ -453,13 +463,17 @@ where
state.clear_update_timeout();
}

fn message(
fn message<I>(
&self,
view_state: &mut MemoizedAwaitState,
id_path: &[ViewId],
message: DynMessage,
app_state: &mut State,
) -> MessageResult<Action, DynMessage> {
init_future: I,
) -> MessageResult<Action, DynMessage>
where
I: Fn(&Self, Rc<MessageThunk>, &mut State),
{
assert_eq!(id_path.len(), 1);
if id_path[0].routing_id() == view_state.generation {
match *message.downcast().unwrap_throw() {
Expand All @@ -474,6 +488,10 @@ where
view_state.schedule_update = false;
MessageResult::RequestRebuild
}
MemoizedFutureMessage::RequestInit => {
init_future(self, Rc::clone(&view_state.thunk), app_state);
MessageResult::RequestRebuild
}
}
} else {
MessageResult::Stale(message)
Expand Down
1 change: 1 addition & 0 deletions xilem_web/web_examples/fetch/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ workspace = true
console_error_panic_hook = "0.1"
console_log = "1"
gloo-net = { version = "0.6.0", default-features = false, features = ["http", "json", "serde"] }
gloo-timers = { version = "0.3.0", features = ["futures"] }
log = "0.4"
serde = { version = "1", features = ["derive"] }
web-sys = { version = "0.3.69", features = ["Event", "HtmlInputElement"] }
Expand Down
Loading

0 comments on commit 4aed1f3

Please sign in to comment.