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