use_future.rs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. #![allow(missing_docs)]
  2. use crate::{use_callback, use_hook_did_run, use_signal, UseCallback};
  3. use dioxus_core::{
  4. prelude::{flush_sync, spawn, use_hook},
  5. Task,
  6. };
  7. use dioxus_signals::*;
  8. use dioxus_signals::{Readable, Writable};
  9. use std::future::Future;
  10. /// A hook that allows you to spawn a future.
  11. /// This future will **not** run on the server
  12. /// The future is spawned on the next call to `flush_sync` which means that it will not run on the server.
  13. /// To run a future on the server, you should use `spawn` directly.
  14. /// `use_future` **won't return a value**.
  15. /// If you want to return a value from a future, use `use_resource` instead.
  16. /// ```rust
  17. /// fn app() -> Element {
  18. /// let mut count = use_signal(|| 0);
  19. /// let mut running = use_signal(|| true);
  20. /// // `use_future` will spawn an infinitely running future that can be started and stopped
  21. /// use_future(move || async move {
  22. /// loop {
  23. /// if running() {
  24. /// count += 1;
  25. /// }
  26. /// tokio::time::sleep(Duration::from_millis(400)).await;
  27. /// }
  28. /// });
  29. /// rsx! {
  30. /// div {
  31. /// h1 { "Current count: {count}" }
  32. /// button { onclick: move |_| running.toggle(), "Start/Stop the count"}
  33. /// button { onclick: move |_| count.set(0), "Reset the count" }
  34. /// }
  35. /// }
  36. /// }
  37. /// ```
  38. pub fn use_future<F>(mut future: impl FnMut() -> F + 'static) -> UseFuture
  39. where
  40. F: Future + 'static,
  41. {
  42. let mut state = use_signal(|| UseFutureState::Pending);
  43. let mut callback = use_callback(move || {
  44. let fut = future();
  45. spawn(async move {
  46. flush_sync().await;
  47. state.set(UseFutureState::Pending);
  48. fut.await;
  49. state.set(UseFutureState::Ready);
  50. })
  51. });
  52. // Create the task inside a copyvalue so we can reset it in-place later
  53. let task = use_hook(|| CopyValue::new(callback.call()));
  54. // Early returns in dioxus have consequences for use_memo, use_resource, and use_future, etc
  55. // We *don't* want futures to be running if the component early returns. It's a rather weird behavior to have
  56. // use_memo running in the background even if the component isn't hitting those hooks anymore.
  57. //
  58. // React solves this by simply not having early returns interleave with hooks.
  59. // However, since dioxus allows early returns (since we use them for suspense), we need to solve this problem
  60. use_hook_did_run(move |did_run| match did_run {
  61. true => task.peek().resume(),
  62. false => task.peek().pause(),
  63. });
  64. UseFuture {
  65. task,
  66. state,
  67. callback,
  68. }
  69. }
  70. #[derive(Clone, Copy)]
  71. pub struct UseFuture {
  72. task: CopyValue<Task>,
  73. state: Signal<UseFutureState>,
  74. callback: UseCallback<Task>,
  75. }
  76. /// A signal that represents the state of a future
  77. // we might add more states (panicked, etc)
  78. #[derive(Clone, Copy, PartialEq, Hash, Eq, Debug)]
  79. pub enum UseFutureState {
  80. /// The future is still running
  81. Pending,
  82. /// The future has been forcefully stopped
  83. Stopped,
  84. /// The future has been paused, tempoarily
  85. Paused,
  86. /// The future has completed
  87. Ready,
  88. }
  89. impl UseFuture {
  90. /// Restart the future with new dependencies.
  91. ///
  92. /// Will not cancel the previous future, but will ignore any values that it
  93. /// generates.
  94. pub fn restart(&mut self) {
  95. self.task.write().cancel();
  96. let new_task = self.callback.call();
  97. self.task.set(new_task);
  98. }
  99. /// Forcefully cancel a future
  100. pub fn cancel(&mut self) {
  101. self.state.set(UseFutureState::Stopped);
  102. self.task.write().cancel();
  103. }
  104. /// Pause the future
  105. pub fn pause(&mut self) {
  106. self.state.set(UseFutureState::Paused);
  107. self.task.write().pause();
  108. }
  109. /// Resume the future
  110. pub fn resume(&mut self) {
  111. if self.finished() {
  112. return;
  113. }
  114. self.state.set(UseFutureState::Pending);
  115. self.task.write().resume();
  116. }
  117. /// Get a handle to the inner task backing this future
  118. /// Modify the task through this handle will cause inconsistent state
  119. pub fn task(&self) -> Task {
  120. self.task.cloned()
  121. }
  122. /// Is the future currently finished running?
  123. ///
  124. /// Reading this does not subscribe to the future's state
  125. pub fn finished(&self) -> bool {
  126. matches!(
  127. *self.state.peek(),
  128. UseFutureState::Ready | UseFutureState::Stopped
  129. )
  130. }
  131. /// Get the current state of the future.
  132. pub fn state(&self) -> ReadOnlySignal<UseFutureState> {
  133. self.state.into()
  134. }
  135. }