Rip out async components
This commit is contained in:
parent
4147498041
commit
cbd88bbcc3
|
@ -10,7 +10,7 @@ struct ListBreeds {
|
|||
message: HashMap<String, Vec<String>>,
|
||||
}
|
||||
|
||||
async fn app_root(cx: Scope<'_>) -> Element {
|
||||
fn app_root(cx: Scope<'_>) -> Element {
|
||||
let breed = use_state(cx, || "deerhound".to_string());
|
||||
|
||||
let breeds = use_future!(cx, || async move {
|
||||
|
@ -21,13 +21,13 @@ async fn app_root(cx: Scope<'_>) -> Element {
|
|||
.await
|
||||
});
|
||||
|
||||
match breeds.await {
|
||||
Ok(breeds) => cx.render(rsx! {
|
||||
match breeds.suspend()? {
|
||||
Ok(breed_list) => cx.render(rsx! {
|
||||
div { height: "500px",
|
||||
h1 { "Select a dog breed!" }
|
||||
div { display: "flex",
|
||||
ul { flex: "50%",
|
||||
for cur_breed in breeds.message.keys().take(10) {
|
||||
for cur_breed in breed_list.message.keys().take(10) {
|
||||
li { key: "{cur_breed}",
|
||||
button {
|
||||
onclick: move |_| breed.set(cur_breed.clone()),
|
||||
|
@ -50,7 +50,7 @@ struct DogApi {
|
|||
}
|
||||
|
||||
#[inline_props]
|
||||
async fn breed_pic(cx: Scope, breed: String) -> Element {
|
||||
fn breed_pic(cx: Scope, breed: String) -> Element {
|
||||
let fut = use_future!(cx, |breed| async move {
|
||||
reqwest::get(format!("https://dog.ceo/api/breed/{breed}/images/random"))
|
||||
.await
|
||||
|
@ -59,7 +59,7 @@ async fn breed_pic(cx: Scope, breed: String) -> Element {
|
|||
.await
|
||||
});
|
||||
|
||||
match fut.await {
|
||||
match fut.suspend()? {
|
||||
Ok(resp) => render! {
|
||||
div {
|
||||
button {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use std::{marker::PhantomData, panic::AssertUnwindSafe};
|
||||
use std::{ panic::AssertUnwindSafe};
|
||||
|
||||
use crate::{
|
||||
innerlude::Scoped,
|
||||
nodes::{ComponentReturn, RenderReturn},
|
||||
nodes::RenderReturn,
|
||||
scopes::{Scope, ScopeState},
|
||||
Element,
|
||||
};
|
||||
|
@ -18,19 +18,15 @@ pub(crate) unsafe trait AnyProps<'a> {
|
|||
unsafe fn memoize(&self, other: &dyn AnyProps) -> bool;
|
||||
}
|
||||
|
||||
pub(crate) struct VProps<'a, P, A, F: ComponentReturn<'a, A> = Element<'a>> {
|
||||
pub render_fn: fn(Scope<'a, P>) -> F,
|
||||
pub(crate) struct VProps<'a, P> {
|
||||
pub render_fn: fn(Scope<'a, P>) -> Element<'a>,
|
||||
pub memo: unsafe fn(&P, &P) -> bool,
|
||||
pub props: P,
|
||||
_marker: PhantomData<A>,
|
||||
}
|
||||
|
||||
impl<'a, P, A, F> VProps<'a, P, A, F>
|
||||
where
|
||||
F: ComponentReturn<'a, A>,
|
||||
{
|
||||
impl<'a, P> VProps<'a, P> {
|
||||
pub(crate) fn new(
|
||||
render_fn: fn(Scope<'a, P>) -> F,
|
||||
render_fn: fn(Scope<'a, P>) -> Element<'a>,
|
||||
memo: unsafe fn(&P, &P) -> bool,
|
||||
props: P,
|
||||
) -> Self {
|
||||
|
@ -38,15 +34,11 @@ where
|
|||
render_fn,
|
||||
memo,
|
||||
props,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, P, A, F> AnyProps<'a> for VProps<'a, P, A, F>
|
||||
where
|
||||
F: ComponentReturn<'a, A>,
|
||||
{
|
||||
unsafe impl<'a, P> AnyProps<'a> for VProps<'a, P> {
|
||||
fn props_ptr(&self) -> *const () {
|
||||
&self.props as *const _ as *const ()
|
||||
}
|
||||
|
@ -69,12 +61,12 @@ where
|
|||
scope: cx,
|
||||
});
|
||||
|
||||
(self.render_fn)(scope).into_return(cx)
|
||||
(self.render_fn)(scope)
|
||||
}));
|
||||
|
||||
match res {
|
||||
Ok(e) => e,
|
||||
Err(_) => RenderReturn::default(),
|
||||
Ok(Some(e)) => RenderReturn::Ready(e),
|
||||
_ => RenderReturn::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -514,7 +514,6 @@ impl<'b> VirtualDom {
|
|||
match unsafe { self.run_scope(scope).extend_lifetime_ref() } {
|
||||
Ready(t) => self.mount_component(scope, template, t, idx),
|
||||
Aborted(t) => self.mount_aborted(template, t),
|
||||
Pending(_) => self.mount_async(template, idx, scope),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -591,24 +590,6 @@ impl<'b> VirtualDom {
|
|||
1
|
||||
}
|
||||
|
||||
/// Take the rendered nodes from a component and handle them if they were async
|
||||
///
|
||||
/// IE simply assign an ID to the placeholder
|
||||
fn mount_async(&mut self, template: &VNode, idx: usize, scope: ScopeId) -> usize {
|
||||
let new_id = self.next_element(template, template.template.get().node_paths[idx]);
|
||||
|
||||
// Set the placeholder of the scope
|
||||
self.scopes[scope].placeholder.set(Some(new_id));
|
||||
|
||||
// Since the placeholder is already in the DOM, we don't create any new nodes
|
||||
self.mutations.push(AssignId {
|
||||
id: new_id,
|
||||
path: &template.template.get().node_paths[idx][1..],
|
||||
});
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
fn set_slot(
|
||||
&mut self,
|
||||
template: &'b VNode<'b>,
|
||||
|
|
|
@ -30,7 +30,7 @@ impl<'b> VirtualDom {
|
|||
.try_load_node()
|
||||
.expect("Call rebuild before diffing");
|
||||
|
||||
use RenderReturn::{Aborted, Pending, Ready};
|
||||
use RenderReturn::{Aborted, Ready};
|
||||
|
||||
match (old, new) {
|
||||
// Normal pathway
|
||||
|
@ -42,29 +42,14 @@ impl<'b> VirtualDom {
|
|||
// Just move over the placeholder
|
||||
(Aborted(l), Aborted(r)) => r.id.set(l.id.get()),
|
||||
|
||||
// Becomes async, do nothing while we wait
|
||||
(Ready(_nodes), Pending(_fut)) => self.diff_ok_to_async(_nodes, scope),
|
||||
|
||||
// Placeholder becomes something
|
||||
// We should also clear the error now
|
||||
(Aborted(l), Ready(r)) => self.replace_placeholder(l, [r]),
|
||||
|
||||
(Aborted(_), Pending(_)) => todo!("async should not resolve here"),
|
||||
(Pending(_), Ready(_)) => todo!("async should not resolve here"),
|
||||
(Pending(_), Aborted(_)) => todo!("async should not resolve here"),
|
||||
(Pending(_), Pending(_)) => {
|
||||
// All suspense should resolve before we diff it again
|
||||
panic!("Should not roll from suspense to suspense.");
|
||||
}
|
||||
};
|
||||
}
|
||||
self.scope_stack.pop();
|
||||
}
|
||||
|
||||
fn diff_ok_to_async(&mut self, _new: &'b VNode<'b>, _scope: ScopeId) {
|
||||
//
|
||||
}
|
||||
|
||||
fn diff_ok_to_err(&mut self, l: &'b VNode<'b>, p: &'b VPlaceholder) {
|
||||
let id = self.next_null();
|
||||
p.id.set(Some(id));
|
||||
|
@ -735,7 +720,6 @@ impl<'b> VirtualDom {
|
|||
match unsafe { self.scopes[scope].root_node().extend_lifetime_ref() } {
|
||||
RenderReturn::Ready(node) => self.push_all_real_nodes(node),
|
||||
RenderReturn::Aborted(_node) => todo!(),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -937,7 +921,6 @@ impl<'b> VirtualDom {
|
|||
match unsafe { self.scopes[scope].root_node().extend_lifetime_ref() } {
|
||||
RenderReturn::Ready(t) => self.remove_node(t, gen_muts),
|
||||
RenderReturn::Aborted(placeholder) => self.remove_placeholder(placeholder, gen_muts),
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
// Restore the props back to the vcomponent in case it gets rendered again
|
||||
|
|
|
@ -7,7 +7,6 @@ use std::{
|
|||
any::{Any, TypeId},
|
||||
cell::{Cell, RefCell, UnsafeCell},
|
||||
fmt::{Arguments, Debug},
|
||||
future::Future,
|
||||
};
|
||||
|
||||
pub type TemplateId = &'static str;
|
||||
|
@ -28,9 +27,6 @@ pub enum RenderReturn<'a> {
|
|||
/// In its place we've produced a placeholder to locate its spot in the dom when
|
||||
/// it recovers.
|
||||
Aborted(VPlaceholder),
|
||||
|
||||
/// An ongoing future that will resolve to a [`Element`]
|
||||
Pending(BumpBox<'a, dyn Future<Output = Element<'a>> + 'a>),
|
||||
}
|
||||
|
||||
impl<'a> Default for RenderReturn<'a> {
|
||||
|
@ -688,32 +684,6 @@ impl<T: Any + PartialEq + 'static> AnyValue for T {
|
|||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait ComponentReturn<'a, A = ()> {
|
||||
fn into_return(self, cx: &'a ScopeState) -> RenderReturn<'a>;
|
||||
}
|
||||
|
||||
impl<'a> ComponentReturn<'a> for Element<'a> {
|
||||
fn into_return(self, _cx: &ScopeState) -> RenderReturn<'a> {
|
||||
match self {
|
||||
Some(node) => RenderReturn::Ready(node),
|
||||
None => RenderReturn::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct AsyncMarker;
|
||||
impl<'a, F> ComponentReturn<'a, AsyncMarker> for F
|
||||
where
|
||||
F: Future<Output = Element<'a>> + 'a,
|
||||
{
|
||||
fn into_return(self, cx: &'a ScopeState) -> RenderReturn<'a> {
|
||||
let f: &mut dyn Future<Output = Element<'a>> = cx.bump().alloc(self);
|
||||
RenderReturn::Pending(unsafe { BumpBox::from_raw(f) })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RenderReturn<'a> {
|
||||
pub(crate) unsafe fn extend_lifetime_ref<'c>(&self) -> &'c RenderReturn<'c> {
|
||||
unsafe { std::mem::transmute(self) }
|
||||
|
|
|
@ -70,6 +70,6 @@ impl EmptyBuilder {
|
|||
|
||||
/// This utility function launches the builder method so rsx! and html! macros can use the typed-builder pattern
|
||||
/// to initialize a component's props.
|
||||
pub fn fc_to_builder<'a, A, T: Properties + 'a>(_: fn(Scope<'a, T>) -> A) -> T::Builder {
|
||||
pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Scope<'a, T>) -> Element<'a>) -> T::Builder {
|
||||
T::builder()
|
||||
}
|
||||
|
|
|
@ -18,9 +18,6 @@ pub(crate) enum SchedulerMsg {
|
|||
|
||||
/// A task has woken and needs to be progressed
|
||||
TaskNotified(TaskId),
|
||||
|
||||
/// A task has woken and needs to be progressed
|
||||
SuspenseNotified(SuspenseId),
|
||||
}
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
@ -30,9 +27,6 @@ pub(crate) struct Scheduler {
|
|||
|
||||
/// Tasks created with cx.spawn
|
||||
pub tasks: RefCell<Slab<LocalTask>>,
|
||||
|
||||
/// Async components
|
||||
pub leaves: RefCell<Slab<SuspenseLeaf>>,
|
||||
}
|
||||
|
||||
impl Scheduler {
|
||||
|
@ -40,7 +34,6 @@ impl Scheduler {
|
|||
Rc::new(Scheduler {
|
||||
sender,
|
||||
tasks: RefCell::new(Slab::new()),
|
||||
leaves: RefCell::new(Slab::new()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,23 +36,3 @@ impl SuspenseContext {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SuspenseLeaf {
|
||||
pub(crate) scope_id: ScopeId,
|
||||
pub(crate) notified: Cell<bool>,
|
||||
pub(crate) task: *mut dyn Future<Output = Element<'static>>,
|
||||
pub(crate) waker: Waker,
|
||||
}
|
||||
|
||||
pub struct SuspenseHandle {
|
||||
pub(crate) id: SuspenseId,
|
||||
pub(crate) tx: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
|
||||
}
|
||||
|
||||
impl ArcWake for SuspenseHandle {
|
||||
fn wake_by_ref(arc_self: &Arc<Self>) {
|
||||
_ = arc_self
|
||||
.tx
|
||||
.unbounded_send(SchedulerMsg::SuspenseNotified(arc_self.id));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,5 @@
|
|||
use futures_util::FutureExt;
|
||||
use std::{
|
||||
rc::Rc,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
innerlude::{Mutation, Mutations, SuspenseContext},
|
||||
nodes::RenderReturn,
|
||||
ScopeId, TaskId, VNode, VirtualDom,
|
||||
};
|
||||
|
||||
use super::SuspenseId;
|
||||
use crate::{innerlude::SuspenseContext, ScopeId, TaskId, VirtualDom};
|
||||
use std::{rc::Rc, task::Context};
|
||||
|
||||
impl VirtualDom {
|
||||
/// Handle notifications by tasks inside the scheduler
|
||||
|
@ -44,68 +33,4 @@ impl VirtualDom {
|
|||
.consume_context::<Rc<SuspenseContext>>()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn handle_suspense_wakeup(&mut self, id: SuspenseId) {
|
||||
let leaves = self.scheduler.leaves.borrow_mut();
|
||||
let leaf = leaves.get(id.0).unwrap();
|
||||
|
||||
let scope_id = leaf.scope_id;
|
||||
|
||||
// todo: cache the waker
|
||||
let mut cx = Context::from_waker(&leaf.waker);
|
||||
|
||||
// Safety: the future is always pinned to the bump arena
|
||||
let mut pinned = unsafe { std::pin::Pin::new_unchecked(&mut *leaf.task) };
|
||||
let as_pinned_mut = &mut pinned;
|
||||
|
||||
// the component finished rendering and gave us nodes
|
||||
// we should attach them to that component and then render its children
|
||||
// continue rendering the tree until we hit yet another suspended component
|
||||
if let Poll::Ready(new_nodes) = as_pinned_mut.poll_unpin(&mut cx) {
|
||||
let fiber = self.acquire_suspense_boundary(leaf.scope_id);
|
||||
|
||||
let scope = &self.scopes[scope_id];
|
||||
let arena = scope.current_frame();
|
||||
|
||||
let ret = arena.bump().alloc(match new_nodes {
|
||||
Some(new) => RenderReturn::Ready(new),
|
||||
None => RenderReturn::default(),
|
||||
});
|
||||
|
||||
arena.node.set(ret);
|
||||
|
||||
fiber.waiting_on.borrow_mut().remove(&id);
|
||||
|
||||
if let RenderReturn::Ready(template) = ret {
|
||||
let mutations_ref = &mut fiber.mutations.borrow_mut();
|
||||
let mutations = &mut **mutations_ref;
|
||||
let template: &VNode = unsafe { std::mem::transmute(template) };
|
||||
let mutations: &mut Mutations = unsafe { std::mem::transmute(mutations) };
|
||||
|
||||
std::mem::swap(&mut self.mutations, mutations);
|
||||
|
||||
let place_holder_id = scope.placeholder.get().unwrap();
|
||||
self.scope_stack.push(scope_id);
|
||||
|
||||
drop(leaves);
|
||||
|
||||
let created = self.create(template);
|
||||
self.scope_stack.pop();
|
||||
mutations.push(Mutation::ReplaceWith {
|
||||
id: place_holder_id,
|
||||
m: created,
|
||||
});
|
||||
|
||||
for leaf in self.collected_leaves.drain(..) {
|
||||
fiber.waiting_on.borrow_mut().insert(leaf);
|
||||
}
|
||||
|
||||
std::mem::swap(&mut self.mutations, mutations);
|
||||
|
||||
if fiber.waiting_on.borrow().is_empty() {
|
||||
self.finished_fibers.push(fiber.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,18 +2,10 @@ use crate::{
|
|||
any_props::AnyProps,
|
||||
bump_frame::BumpFrame,
|
||||
innerlude::DirtyScope,
|
||||
innerlude::{SuspenseHandle, SuspenseId, SuspenseLeaf},
|
||||
nodes::RenderReturn,
|
||||
scopes::{ScopeId, ScopeState},
|
||||
virtual_dom::VirtualDom,
|
||||
};
|
||||
use futures_util::FutureExt;
|
||||
use std::{
|
||||
mem,
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
impl VirtualDom {
|
||||
pub(super) fn new_scope(
|
||||
|
@ -58,7 +50,7 @@ impl VirtualDom {
|
|||
// Remove all the outdated listeners
|
||||
self.ensure_drop_safety(scope_id);
|
||||
|
||||
let mut new_nodes = unsafe {
|
||||
let new_nodes = unsafe {
|
||||
self.scopes[scope_id].previous_frame().bump_mut().reset();
|
||||
|
||||
let scope = &self.scopes[scope_id];
|
||||
|
@ -67,65 +59,11 @@ impl VirtualDom {
|
|||
|
||||
// safety: due to how we traverse the tree, we know that the scope is not currently aliased
|
||||
let props: &dyn AnyProps = scope.props.as_ref().unwrap().as_ref();
|
||||
let props: &dyn AnyProps = mem::transmute(props);
|
||||
let props: &dyn AnyProps = std::mem::transmute(props);
|
||||
|
||||
props.render(scope).extend_lifetime()
|
||||
};
|
||||
|
||||
// immediately resolve futures that can be resolved
|
||||
if let RenderReturn::Pending(task) = &mut new_nodes {
|
||||
let mut leaves = self.scheduler.leaves.borrow_mut();
|
||||
|
||||
let entry = leaves.vacant_entry();
|
||||
let suspense_id = SuspenseId(entry.key());
|
||||
|
||||
let leaf = SuspenseLeaf {
|
||||
scope_id,
|
||||
task: task.as_mut(),
|
||||
notified: Default::default(),
|
||||
waker: futures_util::task::waker(Arc::new(SuspenseHandle {
|
||||
id: suspense_id,
|
||||
tx: self.scheduler.sender.clone(),
|
||||
})),
|
||||
};
|
||||
|
||||
let mut cx = Context::from_waker(&leaf.waker);
|
||||
|
||||
// safety: the task is already pinned in the bump arena
|
||||
let mut pinned = unsafe { Pin::new_unchecked(task.as_mut()) };
|
||||
|
||||
// Keep polling until either we get a value or the future is not ready
|
||||
loop {
|
||||
match pinned.poll_unpin(&mut cx) {
|
||||
// If nodes are produced, then set it and we can break
|
||||
Poll::Ready(nodes) => {
|
||||
new_nodes = match nodes {
|
||||
Some(nodes) => RenderReturn::Ready(nodes),
|
||||
None => RenderReturn::default(),
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// If no nodes are produced but the future woke up immediately, then try polling it again
|
||||
// This circumvents things like yield_now, but is important is important when rendering
|
||||
// components that are just a stream of immediately ready futures
|
||||
_ if leaf.notified.get() => {
|
||||
leaf.notified.set(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If no nodes are produced, then we need to wait for the future to be woken up
|
||||
// Insert the future into fiber leaves and break
|
||||
_ => {
|
||||
entry.insert(leaf);
|
||||
self.collected_leaves.push(suspense_id);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let scope = &self.scopes[scope_id];
|
||||
|
||||
// We write on top of the previous frame and then make it the current by pushing the generation forward
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
innerlude::{DynamicNode, EventHandler, VComponent, VText},
|
||||
innerlude::{ErrorBoundary, Scheduler, SchedulerMsg},
|
||||
lazynodes::LazyNodes,
|
||||
nodes::{ComponentReturn, IntoAttributeValue, IntoDynNode, RenderReturn},
|
||||
nodes::{IntoAttributeValue, IntoDynNode, RenderReturn},
|
||||
AnyValue, Attribute, AttributeValue, Element, Event, Properties, TaskId,
|
||||
};
|
||||
use bumpalo::{boxed::Box as BumpBox, Bump};
|
||||
|
@ -574,9 +574,9 @@ impl<'src> ScopeState {
|
|||
/// fn(Scope<Props>) -> Element;
|
||||
/// async fn(Scope<Props<'_>>) -> Element;
|
||||
/// ```
|
||||
pub fn component<P, A, F: ComponentReturn<'src, A>>(
|
||||
pub fn component<P>(
|
||||
&'src self,
|
||||
component: fn(Scope<'src, P>) -> F,
|
||||
component: fn(Scope<'src, P>) -> Element<'src>,
|
||||
props: P,
|
||||
fn_name: &'static str,
|
||||
) -> DynamicNode<'src>
|
||||
|
|
|
@ -335,7 +335,8 @@ impl VirtualDom {
|
|||
/// Determine if the tree is at all suspended. Used by SSR and other outside mechanisms to determine if the tree is
|
||||
/// ready to be rendered.
|
||||
pub fn has_suspended_work(&self) -> bool {
|
||||
!self.scheduler.leaves.borrow().is_empty()
|
||||
todo!()
|
||||
// !self.scheduler.leaves.borrow().is_empty()
|
||||
}
|
||||
|
||||
/// Call a listener inside the VirtualDom with data from outside the VirtualDom.
|
||||
|
@ -485,7 +486,6 @@ impl VirtualDom {
|
|||
Some(msg) => match msg {
|
||||
SchedulerMsg::Immediate(id) => self.mark_dirty(id),
|
||||
SchedulerMsg::TaskNotified(task) => self.handle_task_wakeup(task),
|
||||
SchedulerMsg::SuspenseNotified(id) => self.handle_suspense_wakeup(id),
|
||||
},
|
||||
|
||||
// If they're not ready, then we should wait for them to be ready
|
||||
|
@ -513,7 +513,6 @@ impl VirtualDom {
|
|||
match msg {
|
||||
SchedulerMsg::Immediate(id) => self.mark_dirty(id),
|
||||
SchedulerMsg::TaskNotified(task) => self.handle_task_wakeup(task),
|
||||
SchedulerMsg::SuspenseNotified(id) => self.handle_suspense_wakeup(id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -574,7 +573,6 @@ impl VirtualDom {
|
|||
}
|
||||
// If an error occurs, we should try to render the default error component and context where the error occured
|
||||
RenderReturn::Aborted(_placeholder) => panic!("Cannot catch errors during rebuild"),
|
||||
RenderReturn::Pending(_) => unreachable!("Root scope cannot be an async component"),
|
||||
}
|
||||
|
||||
self.finalize()
|
||||
|
@ -680,11 +678,6 @@ impl VirtualDom {
|
|||
continue;
|
||||
}
|
||||
|
||||
// If there's no pending suspense, then we have no reason to wait for anything
|
||||
if self.scheduler.leaves.borrow().is_empty() {
|
||||
return self.finalize();
|
||||
}
|
||||
|
||||
// Poll the suspense leaves in the meantime
|
||||
let mut work = self.wait_for_work();
|
||||
|
||||
|
|
|
@ -59,42 +59,45 @@ fn app(cx: Scope) -> Element {
|
|||
}
|
||||
|
||||
fn suspense_boundary(cx: Scope) -> Element {
|
||||
cx.use_hook(|| {
|
||||
cx.provide_context(Rc::new(SuspenseContext::new(cx.scope_id())));
|
||||
});
|
||||
todo!()
|
||||
// cx.use_hook(|| {
|
||||
// cx.provide_context(Rc::new(SuspenseContext::new(cx.scope_id())));
|
||||
// });
|
||||
|
||||
// Ensure the right types are found
|
||||
cx.has_context::<Rc<SuspenseContext>>().unwrap();
|
||||
// // Ensure the right types are found
|
||||
// cx.has_context::<Rc<SuspenseContext>>().unwrap();
|
||||
|
||||
cx.render(rsx!(async_child {}))
|
||||
// cx.render(rsx!(async_child {}))
|
||||
}
|
||||
|
||||
async fn async_child(cx: Scope<'_>) -> Element {
|
||||
use_future!(cx, || tokio::time::sleep(Duration::from_millis(10))).await;
|
||||
cx.render(rsx!(async_text {}))
|
||||
fn async_child(cx: Scope<'_>) -> Element {
|
||||
todo!()
|
||||
// use_future!(cx, || tokio::time::sleep(Duration::from_millis(10))).await;
|
||||
// cx.render(rsx!(async_text {}))
|
||||
}
|
||||
|
||||
async fn async_text(cx: Scope<'_>) -> Element {
|
||||
let username = use_future!(cx, || async {
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
"async child 1"
|
||||
});
|
||||
fn async_text(cx: Scope<'_>) -> Element {
|
||||
todo!()
|
||||
// let username = use_future!(cx, || async {
|
||||
// tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
// "async child 1"
|
||||
// });
|
||||
|
||||
let age = use_future!(cx, || async {
|
||||
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
|
||||
1234
|
||||
});
|
||||
// let age = use_future!(cx, || async {
|
||||
// tokio::time::sleep(std::time::Duration::from_secs(2)).await;
|
||||
// 1234
|
||||
// });
|
||||
|
||||
let (_user, _age) = use_future!(cx, || async {
|
||||
tokio::join!(
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)),
|
||||
tokio::time::sleep(std::time::Duration::from_secs(2))
|
||||
);
|
||||
("async child 1", 1234)
|
||||
})
|
||||
.await;
|
||||
// let (_user, _age) = use_future!(cx, || async {
|
||||
// tokio::join!(
|
||||
// tokio::time::sleep(std::time::Duration::from_secs(1)),
|
||||
// tokio::time::sleep(std::time::Duration::from_secs(2))
|
||||
// );
|
||||
// ("async child 1", 1234)
|
||||
// })
|
||||
// .await;
|
||||
|
||||
let (username, age) = tokio::join!(username.into_future(), age.into_future());
|
||||
// let (username, age) = tokio::join!(username.into_future(), age.into_future());
|
||||
|
||||
cx.render(rsx!( div { "Hello! {username}, you are {age}, {_user} {_age}" } ))
|
||||
// cx.render(rsx!( div { "Hello! {username}, you are {age}, {_user} {_age}" } ))
|
||||
}
|
||||
|
|
|
@ -167,6 +167,10 @@ impl<T> UseFuture<T> {
|
|||
(Some(_), None) => UseFutureState::Pending,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn suspend(&self) -> Option<&T> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IntoFuture for &'a UseFuture<T> {
|
||||
|
|
Loading…
Reference in New Issue