1//! Futures support for reactive scopes.
23#![deny(missing_debug_implementations)]
4#![warn(missing_docs)]
56mod suspense;
78use std::pin::Pin;
9use std::task::{Context, Poll};
1011use futures::future::abortable;
12use futures::stream::Abortable;
13use futures::Future;
14use pin_project::pin_project;
15use sycamore_reactive::{on_cleanup, use_current_scope, NodeHandle};
1617pub use self::suspense::*;
1819/// If running on `wasm32` target, does nothing. Otherwise creates a new `tokio::task::LocalSet`
20/// scope.
21pub 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{
28let local = tokio::task::LocalSet::new();
29 local.run_until(fut).await
30}
31}
3233/// Spawns a `!Send` future.
34///
35/// This will not auto cancel the task if the scope in which it is created is destroyed.
36/// For this purpose, use [`spawn_local_scoped`] instead.
37pub fn spawn_local(fut: impl Future<Output = ()> + 'static) {
38#[cfg(any(not(target_arch = "wasm32"), sycamore_force_ssr))]
39tokio::task::spawn_local(fut);
40#[cfg(all(target_arch = "wasm32", not(sycamore_force_ssr)))]
41wasm_bindgen_futures::spawn_local(fut);
42}
4344/// Spawns a `!Send` future on the current scope.
45///
46/// If the scope is destroyed before the future is completed, it is aborted immediately. This
47/// ensures that it is impossible to access any values referencing the scope after they are
48/// destroyed.
49pub fn spawn_local_scoped(fut: impl Future<Output = ()> + 'static) {
50let scoped = ScopedFuture::new_in_current_scope(fut);
51 spawn_local(scoped);
52}
5354/// A wrapper that runs the future on the current scope.
55#[pin_project]
56struct ScopedFuture<T> {
57#[pin]
58task: Abortable<T>,
59 scope: NodeHandle,
60}
6162impl<T: Future> Future for ScopedFuture<T> {
63type Output = ();
6465fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
66let this = self.project();
67 this.scope.run_in(move || this.task.poll(cx).map(|_| ()))
68 }
69}
7071impl<T: Future> ScopedFuture<T> {
72pub fn new_in_current_scope(f: T) -> Self {
73let (abortable, handle) = abortable(f);
74 on_cleanup(move || handle.abort());
7576let scope = use_current_scope();
7778Self {
79 task: abortable,
80 scope,
81 }
82 }
83}