1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use discard::DiscardOnDrop;
use futures::future::{abortable, AbortHandle};
use futures_signals::{
cancelable_future,
signal::{Mutable, Signal},
CancelableFutureHandle,
};
use std::{
future::Future,
sync::atomic::{AtomicUsize, Ordering},
};
use wasm_bindgen_futures::spawn_local;
#[derive(Clone)]
pub struct AsyncLoader {
loading: Mutable<Option<AsyncState>>,
}
impl Drop for AsyncLoader {
fn drop(&mut self) {
self.cancel();
}
}
impl AsyncLoader {
pub fn new() -> Self {
Self {
loading: Mutable::new(None),
}
}
pub fn cancel(&self) {
self.replace(None);
}
fn replace(&self, value: Option<AsyncState>) {
let mut loading = self.loading.lock_mut();
if let Some(state) = loading.as_mut() {
state.handle.abort();
}
*loading = value;
}
pub fn load<F>(&self, fut: F)
where
F: Future<Output = ()> + 'static,
{
let (fut, handle) = abortable(fut);
let state = AsyncState::new(handle);
let id = state.id;
self.replace(Some(state));
let loading = self.loading.clone();
spawn_local(async move {
match fut.await {
Ok(()) => {
let mut loading = loading.lock_mut();
if let Some(current_id) = loading.as_ref().map(|x| x.id) {
if current_id == id {
*loading = None;
}
}
}
Err(_) => {}
}
});
}
pub fn is_loading(&self) -> impl Signal<Item = bool> {
self.loading.signal_ref(|x| x.is_some())
}
}
struct AsyncState {
id: usize,
handle: AbortHandle,
}
impl AsyncState {
fn new(handle: AbortHandle) -> Self {
static ID: AtomicUsize = AtomicUsize::new(0);
let id = ID.fetch_add(1, Ordering::SeqCst);
Self { id, handle }
}
}
#[inline]
pub fn spawn_future<F>(future: F) -> DiscardOnDrop<CancelableFutureHandle>
where
F: Future<Output = ()> + 'static,
{
let (handle, future) = cancelable_future(future, || ());
spawn_local(future);
handle
}