From b42992bad1a6ffeadf74b02a73faa7d91476cc2b Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 4 Mar 2024 15:38:28 -0600 Subject: [PATCH] add more internal documentation about tasks --- packages/core/src/dirty_scope.rs | 35 ++++++++++++++++++++++++++++++ packages/core/src/render_signal.rs | 2 ++ packages/core/src/virtual_dom.rs | 3 ++- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/packages/core/src/dirty_scope.rs b/packages/core/src/dirty_scope.rs index a740f0c5c..0fa2bece7 100644 --- a/packages/core/src/dirty_scope.rs +++ b/packages/core/src/dirty_scope.rs @@ -1,3 +1,32 @@ +//! Dioxus resolves scopes in a specific order to avoid unexpected behavior. All tasks are resolved in the order of height. Scopes that are higher up in the tree are resolved first. +//! When a scope that is higher up in the tree is rerendered, it may drop scopes lower in the tree along with their tasks. +//! +//! ```rust +//! use dioxus::prelude::*; +//! +//! fn app() -> Element { +//! let vec = use_signal(|| vec![0; 10]); +//! rsx! { +//! // If the length of the vec shrinks we need to make sure that the children are dropped along with their tasks the new state of the vec is read +//! for idx in 0..vec.len() { +//! Child { idx, vec } +//! } +//! } +//! } +//! +//! #[component] +//! fn Child(vec: Signal>, idx: usize) -> Element { +//! use_hook(|| { +//! spawn(async { +//! // If we let this task run after the child is dropped, it will panic. +//! println!("Task {}", vec.read()[idx]); +//! }); +//! }); +//! +//! rsx! {} +//! } +//! ``` + use crate::ScopeId; use crate::Task; use std::borrow::Borrow; @@ -49,6 +78,7 @@ pub struct DirtyScopes { } impl DirtyScopes { + /// Queue a task to be polled pub fn queue_task(&mut self, task: Task, order: ScopeOrder) { match self.tasks.get(&order) { Some(scope) => scope.queue_task(task), @@ -60,22 +90,27 @@ impl DirtyScopes { } } + /// Queue a scope to be rerendered pub fn queue_scope(&mut self, order: ScopeOrder) { self.scopes.insert(order); } + /// Check if there are any dirty scopes pub fn has_dirty_scopes(&self) -> bool { !self.scopes.is_empty() } + /// Take any tasks from the highest scope pub fn pop_task(&mut self) -> Option { self.tasks.pop_first() } + /// Take the highest scope that needs to be rerendered pub fn pop_scope(&mut self) -> Option { self.scopes.pop_first() } + /// Take any work from the highest scope. This may include rerunning the scope and/or running tasks pub fn pop_work(&mut self) -> Option { let dirty_scope = self.scopes.first(); let dirty_task = self.tasks.first(); diff --git a/packages/core/src/render_signal.rs b/packages/core/src/render_signal.rs index 860885de1..854167c49 100644 --- a/packages/core/src/render_signal.rs +++ b/packages/core/src/render_signal.rs @@ -1,3 +1,5 @@ +//! In dioxus, effects are run using normal async functions after a render. [RenderSignalFuture] is a future that resolves after a render has passed. + use std::cell::RefCell; use std::future::Future; use std::pin::Pin; diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 0d594c43b..e5905d560 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -439,7 +439,7 @@ impl VirtualDom { self.poll_tasks().await; } - /// + /// Poll the scheduler for any work async fn poll_tasks(&mut self) { loop { // Process all events - Scopes are marked dirty, etc @@ -454,6 +454,7 @@ impl VirtualDom { // Make sure we set the runtime since we're running user code let _runtime = RuntimeGuard::new(self.runtime.clone()); + /// There isn't any more work we can do synchronously. Wait for any new work to be ready match self.rx.next().await.expect("channel should never close") { SchedulerMsg::Immediate(id) => self.mark_dirty(id), SchedulerMsg::TaskNotified(id) => {