1#![deny(missing_debug_implementations)]
4#![warn(missing_docs)]
5
6mod suspense;
7
8use std::pin::Pin;
9use std::task::{Context, Poll};
10
11use futures::future::abortable;
12use futures::stream::Abortable;
13use futures::Future;
14use pin_project::pin_project;
15use sycamore_reactive::{on_cleanup, use_current_scope, NodeHandle};
16
17pub use self::suspense::*;
18
19pub async fn provide_executor_scope<U>(fut: impl Future<Output = U>) -> U {
22 #[cfg(all(target_arch = "wasm32", not(sycamore_force_ssr)))]
23 {
24 fut.await
25 }
26 #[cfg(any(not(target_arch = "wasm32"), sycamore_force_ssr))]
27 {
28 let local = tokio::task::LocalSet::new();
29 local.run_until(fut).await
30 }
31}
32
33pub fn spawn_local(fut: impl Future<Output = ()> + 'static) {
38 #[cfg(any(not(target_arch = "wasm32"), sycamore_force_ssr))]
39 tokio::task::spawn_local(fut);
40 #[cfg(all(target_arch = "wasm32", not(sycamore_force_ssr)))]
41 wasm_bindgen_futures::spawn_local(fut);
42}
43
44#[cfg_attr(debug_assertions, track_caller)]
50pub fn spawn_local_scoped(fut: impl Future<Output = ()> + 'static) {
51 let scoped = ScopedFuture::new_in_current_scope(fut);
52 spawn_local(scoped);
53}
54
55#[pin_project]
57struct ScopedFuture<T> {
58 #[pin]
59 task: Abortable<T>,
60 scope: NodeHandle,
61}
62
63impl<T: Future> Future for ScopedFuture<T> {
64 type Output = ();
65
66 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
67 let this = self.project();
68 this.scope.run_in(move || this.task.poll(cx).map(|_| ()))
69 }
70}
71
72impl<T: Future> ScopedFuture<T> {
73 #[cfg_attr(debug_assertions, track_caller)]
74 pub fn new_in_current_scope(f: T) -> Self {
75 let (abortable, handle) = abortable(f);
76 on_cleanup(move || handle.abort());
77
78 let scope = use_current_scope();
79
80 Self {
81 task: abortable,
82 scope,
83 }
84 }
85}