@@ -6,6 +6,7 @@ use crate::{
66use futures:: { channel:: oneshot, select, FutureExt } ;
77use hydration_context:: SerializedDataId ;
88use leptos_macro:: component;
9+ use or_poisoned:: OrPoisoned ;
910use reactive_graph:: {
1011 computed:: {
1112 suspense:: { LocalResourceNotifier , SuspenseContext } ,
@@ -14,10 +15,10 @@ use reactive_graph::{
1415 effect:: RenderEffect ,
1516 owner:: { provide_context, use_context, Owner } ,
1617 signal:: ArcRwSignal ,
17- traits:: { Dispose , Get , Read , Track , With , WriteValue } ,
18+ traits:: { Dispose , Get , Read , ReadUntracked , Track , With , WriteValue } ,
1819} ;
1920use slotmap:: { DefaultKey , SlotMap } ;
20- use std:: sync:: Arc ;
21+ use std:: sync:: { Arc , Mutex } ;
2122use tachys:: {
2223 either:: Either ,
2324 html:: attribute:: { any_attribute:: AnyAttribute , Attribute } ,
@@ -320,23 +321,66 @@ where
320321
321322 // walk over the tree of children once to make sure that all resource loads are registered
322323 self . children . dry_resolve ( ) ;
324+ let children = Arc :: new ( Mutex :: new ( Some ( self . children ) ) ) ;
323325
324326 // check the set of tasks to see if it is empty, now or later
325327 let eff = reactive_graph:: effect:: Effect :: new_isomorphic ( {
326- move |_| {
327- tasks. track ( ) ;
328- if let Some ( tasks) = tasks. try_read ( ) {
329- if tasks. is_empty ( ) {
330- if let Some ( tx) = tasks_tx. take ( ) {
331- // If the receiver has dropped, it means the ScopedFuture has already
332- // dropped, so it doesn't matter if we manage to send this.
333- _ = tx. send ( ( ) ) ;
334- }
335- if let Some ( tx) = notify_error_boundary. take ( ) {
336- _ = tx. send ( ( ) ) ;
328+ let children = Arc :: clone ( & children) ;
329+ move |double_checking : Option < bool > | {
330+ // on the first run, always track the tasks
331+ if double_checking. is_none ( ) {
332+ tasks. track ( ) ;
333+ }
334+
335+ if let Some ( curr_tasks) = tasks. try_read_untracked ( ) {
336+ if curr_tasks. is_empty ( ) {
337+ if double_checking == Some ( true ) {
338+ // we have finished loading, and checking the children again told us there are
339+ // no more pending tasks. so we can render both the children and the error boundary
340+
341+ if let Some ( tx) = tasks_tx. take ( ) {
342+ // If the receiver has dropped, it means the ScopedFuture has already
343+ // dropped, so it doesn't matter if we manage to send this.
344+ _ = tx. send ( ( ) ) ;
345+ }
346+ if let Some ( tx) = notify_error_boundary. take ( ) {
347+ _ = tx. send ( ( ) ) ;
348+ }
349+ } else {
350+ // release the read guard on tasks, as we'll be updating it again
351+ drop ( curr_tasks) ;
352+ // check the children for additional pending tasks
353+ // the will catch additional resource reads nested inside a conditional depending on initial resource reads
354+ if let Some ( children) =
355+ children. lock ( ) . or_poisoned ( ) . as_mut ( )
356+ {
357+ children. dry_resolve ( ) ;
358+ }
359+
360+ if tasks
361+ . try_read ( )
362+ . map ( |n| n. is_empty ( ) )
363+ . unwrap_or ( false )
364+ {
365+ // there are no additional pending tasks, and we can simply return
366+ if let Some ( tx) = tasks_tx. take ( ) {
367+ // If the receiver has dropped, it means the ScopedFuture has already
368+ // dropped, so it doesn't matter if we manage to send this.
369+ _ = tx. send ( ( ) ) ;
370+ }
371+ if let Some ( tx) = notify_error_boundary. take ( ) {
372+ _ = tx. send ( ( ) ) ;
373+ }
374+ }
375+
376+ // tell ourselves that we're just double-checking
377+ return true ;
337378 }
379+ } else {
380+ tasks. track ( ) ;
338381 }
339382 }
383+ false
340384 }
341385 } ) ;
342386
@@ -362,12 +406,17 @@ where
362406 None
363407 }
364408 _ = tasks_rx => {
409+ let children = {
410+ let mut children_lock = children. lock( ) . or_poisoned( ) ;
411+ children_lock. take( ) . expect( "children should not be removed until we render here" )
412+ } ;
413+
365414 // if we ran this earlier, reactive reads would always be registered as None
366415 // this is fine in the case where we want to use Suspend and .await on some future
367416 // but in situations like a <For each=|| some_resource.snapshot()/> we actually
368417 // want to be able to 1) synchronously read a resource's value, but still 2) wait
369418 // for it to load before we render everything
370- let mut children = Box :: pin( self . children. resolve( ) . fuse( ) ) ;
419+ let mut children = Box :: pin( children. resolve( ) . fuse( ) ) ;
371420
372421 // we continue racing the children against the "do we have any local
373422 // resources?" Future
0 commit comments