Pārlūkot izejas kodu

Rip out async components

Jonathan Kelley 1 gadu atpakaļ
vecāks
revīzija
cbd88bbcc3

+ 6 - 6
examples/dog_app.rs

@@ -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 {

+ 10 - 18
packages/core/src/any_props.rs

@@ -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(),
         }
     }
 }

+ 0 - 19
packages/core/src/create.rs

@@ -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>,

+ 1 - 18
packages/core/src/diff.rs

@@ -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

+ 0 - 30
packages/core/src/nodes.rs

@@ -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) }

+ 1 - 1
packages/core/src/properties.rs

@@ -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()
 }

+ 0 - 7
packages/core/src/scheduler/mod.rs

@@ -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()),
         })
     }
 }

+ 0 - 20
packages/core/src/scheduler/suspense.rs

@@ -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));
-    }
-}

+ 2 - 77
packages/core/src/scheduler/wait.rs

@@ -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 - 64
packages/core/src/scope_arena.rs

@@ -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

+ 3 - 3
packages/core/src/scopes.rs

@@ -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>

+ 2 - 9
packages/core/src/virtual_dom.rs

@@ -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();
 

+ 35 - 32
packages/core/tests/suspense.rs

@@ -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"
-    });
-
-    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 (username, age) = tokio::join!(username.into_future(), age.into_future());
-
-    cx.render(rsx!( div { "Hello! {username}, you are {age}, {_user} {_age}" } ))
+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 (_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());
+
+    // cx.render(rsx!( div { "Hello! {username}, you are {age}, {_user} {_age}" } ))
 }

+ 4 - 0
packages/hooks/src/usefuture.rs

@@ -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> {