yew/suspense/
suspension.rs1use std::cell::RefCell;
2use std::future::Future;
3use std::pin::Pin;
4use std::rc::Rc;
5use std::sync::atomic::{AtomicBool, Ordering};
6use std::task::{Context, Poll};
7
8use thiserror::Error;
9
10use crate::platform::spawn_local;
11use crate::Callback;
12
13thread_local! {
14 static SUSPENSION_ID: RefCell<usize> = RefCell::default();
15}
16
17#[derive(Error, Debug, Clone)]
22#[error("suspend component rendering")]
23pub struct Suspension {
24 id: usize,
25 listeners: Rc<RefCell<Vec<Callback<Self>>>>,
26
27 resumed: Rc<AtomicBool>,
28}
29
30impl PartialEq for Suspension {
31 fn eq(&self, rhs: &Self) -> bool {
32 self.id == rhs.id
33 }
34}
35
36impl Suspension {
37 pub fn new() -> (Self, SuspensionHandle) {
39 let id = SUSPENSION_ID.with(|m| {
40 let mut m = m.borrow_mut();
41 *m += 1;
42
43 *m
44 });
45
46 let self_ = Suspension {
47 id,
48 listeners: Rc::default(),
49 resumed: Rc::default(),
50 };
51
52 (self_.clone(), SuspensionHandle { inner: self_ })
53 }
54
55 pub fn resumed(&self) -> bool {
57 self.resumed.load(Ordering::Relaxed)
58 }
59
60 pub fn from_future(f: impl Future<Output = ()> + 'static) -> Self {
62 let (self_, handle) = Self::new();
63
64 spawn_local(async move {
65 f.await;
66 handle.resume();
67 });
68
69 self_
70 }
71
72 pub(crate) fn listen(&self, cb: Callback<Self>) {
74 if self.resumed() {
75 cb.emit(self.clone());
76 return;
77 }
78
79 let mut listeners = self.listeners.borrow_mut();
80
81 listeners.push(cb);
82 }
83
84 fn resume_by_ref(&self) {
85 if !self.resumed() {
88 self.resumed.store(true, Ordering::Relaxed);
89 let listeners = self.listeners.borrow();
90
91 for listener in listeners.iter() {
92 listener.emit(self.clone());
93 }
94 }
95 }
96}
97
98impl Future for Suspension {
99 type Output = ();
100
101 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
102 if self.resumed() {
103 return Poll::Ready(());
104 }
105
106 let waker = cx.waker().clone();
107 self.listen(Callback::from(move |_| {
108 waker.wake_by_ref();
109 }));
110
111 Poll::Pending
112 }
113}
114
115pub type SuspensionResult<T> = std::result::Result<T, Suspension>;
117
118#[derive(Debug, PartialEq)]
125pub struct SuspensionHandle {
126 inner: Suspension,
127}
128
129impl SuspensionHandle {
130 pub fn resume(self) {
132 self.inner.resume_by_ref();
133 }
134}
135
136impl Drop for SuspensionHandle {
137 fn drop(&mut self) {
138 self.inner.resume_by_ref();
139 }
140}