Skip to content

Commit

Permalink
Add use_future hook to make consuming futures as suspense easier (#…
Browse files Browse the repository at this point in the history
…2609)

* Add `use_suspending_future` hook to make consuming futures as suspense easier

* Add test

* fmt

* use_suspending_future -> use_future

* use_future takes a closure
  • Loading branch information
hamza1311 committed Apr 14, 2022
1 parent b580bd4 commit edeb59d
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 1 deletion.
66 changes: 66 additions & 0 deletions packages/yew/src/suspense/hooks.rs
@@ -0,0 +1,66 @@
#[cfg_attr(documenting, doc(cfg(any(target_arch = "wasm32", feature = "tokio"))))]
#[cfg(any(target_arch = "wasm32", feature = "tokio"))]
mod feat_futures {
use std::fmt;
use std::future::Future;
use std::ops::Deref;

use yew::prelude::*;
use yew::suspense::{Suspension, SuspensionResult};

/// This hook is used to await a future in a suspending context.
///
/// A [Suspension] is created from the passed future and the result of the future
/// is the output of the suspension.
pub struct UseFutureHandle<O> {
inner: UseStateHandle<Option<O>>,
}

impl<O> Deref for UseFutureHandle<O> {
type Target = O;

fn deref(&self) -> &Self::Target {
&*self.inner.as_ref().unwrap()
}
}

impl<T: fmt::Debug> fmt::Debug for UseFutureHandle<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("UseFutureHandle")
.field("value", &format!("{:?}", self.inner))
.finish()
}
}

#[hook]
pub fn use_future<F, T, O>(f: F) -> SuspensionResult<UseFutureHandle<O>>
where
F: FnOnce() -> T + 'static,
T: Future<Output = O> + 'static,
O: 'static,
{
let output = use_state(|| None);

let suspension = {
let output = output.clone();

use_memo(
move |_| {
Suspension::from_future(async move {
output.set(Some(f().await));
})
},
(),
)
};

if suspension.resumed() {
Ok(UseFutureHandle { inner: output })
} else {
Err((*suspension).clone())
}
}
}

#[cfg(any(target_arch = "wasm32", feature = "tokio"))]
pub use feat_futures::*;
2 changes: 2 additions & 0 deletions packages/yew/src/suspense/mod.rs
@@ -1,9 +1,11 @@
//! This module provides suspense support.

mod component;
mod hooks;
mod suspension;

#[cfg(any(feature = "csr", feature = "ssr"))]
pub(crate) use component::BaseSuspense;
pub use component::Suspense;
pub use hooks::*;
pub use suspension::{Suspension, SuspensionHandle, SuspensionResult};
43 changes: 42 additions & 1 deletion packages/yew/tests/suspense.rs
Expand Up @@ -15,7 +15,7 @@ use gloo::timers::future::TimeoutFuture;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::spawn_local;
use web_sys::{HtmlElement, HtmlTextAreaElement};
use yew::suspense::{Suspension, SuspensionResult};
use yew::suspense::{use_future, Suspension, SuspensionResult};

#[wasm_bindgen_test]
async fn suspense_works() {
Expand Down Expand Up @@ -593,3 +593,44 @@ async fn effects_not_run_when_suspended() {
);
assert_eq!(*counter.borrow(), 4); // effects ran 4 times.
}

#[wasm_bindgen_test]
async fn use_suspending_future_works() {
#[function_component(Content)]
fn content() -> HtmlResult {
let _sleep_handle = use_future(|| async move {
TimeoutFuture::new(50).await;
})?;

Ok(html! {
<div>
{"Content"}
</div>
})
}

#[function_component(App)]
fn app() -> Html {
let fallback = html! {<div>{"wait..."}</div>};

html! {
<div id="result">
<Suspense {fallback}>
<Content />
</Suspense>
</div>
}
}

yew::Renderer::<App>::with_root(gloo_utils::document().get_element_by_id("output").unwrap())
.render();

TimeoutFuture::new(10).await;
let result = obtain_result();
assert_eq!(result.as_str(), "<div>wait...</div>");

TimeoutFuture::new(50).await;

let result = obtain_result();
assert_eq!(result.as_str(), r#"<div>Content</div>"#);
}

1 comment on commit edeb59d

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yew master branch benchmarks (Lower is better)

Benchmark suite Current: edeb59d Previous: b580bd4 Ratio
yew-struct-keyed 01_run1k 154.6135 208.092 0.74
yew-struct-keyed 02_replace1k 188.2125 227.942 0.83
yew-struct-keyed 03_update10th1k_x16 355.804 413.986 0.86
yew-struct-keyed 04_select1k 69.139 69.8415 0.99
yew-struct-keyed 05_swap1k 92.145 99.485 0.93
yew-struct-keyed 06_remove-one-1k 28.6175 31.756 0.90
yew-struct-keyed 07_create10k 2871.3095 3377.112 0.85
yew-struct-keyed 08_create1k-after1k_x2 381.354 488.0175 0.78
yew-struct-keyed 09_clear1k_x8 174.562 216.5645 0.81
yew-struct-keyed 21_ready-memory 1.457233428955078 1.457233428955078 1
yew-struct-keyed 22_run-memory 1.6719512939453125 1.6982192993164062 0.98
yew-struct-keyed 23_update5-memory 1.6636428833007812 1.697723388671875 0.98
yew-struct-keyed 24_run5-memory 1.709758758544922 1.9442062377929688 0.88
yew-struct-keyed 25_run-clear-memory 1.3279647827148438 1.3271331787109375 1.00
yew-struct-keyed 31_startup-ci 1734.1999999999998 1741.864 1.00
yew-struct-keyed 32_startup-bt 31.272 46.09600000000001 0.68
yew-struct-keyed 33_startup-mainthreadcost 213.84800000000004 319.6440000000001 0.67
yew-struct-keyed 34_startup-totalbytes 328.7392578125 328.744140625 1.00

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.