11/** @import { ComponentContext, Derived, Effect, Reaction, Signal, Source, Value } from '#client' */
22import { DEV } from 'esm-env' ;
3- import { define_property , get_descriptors , get_prototype_of } from '../shared/utils.js' ;
3+ import { define_property , get_descriptors , get_prototype_of , run_all } from '../shared/utils.js' ;
44import {
55 destroy_block_effect_children ,
66 destroy_effect_children ,
@@ -28,7 +28,7 @@ import {
2828 BOUNDARY_EFFECT ,
2929 YIELD_EFFECT
3030} from './constants.js' ;
31- import { flush_tasks , queue_yield_task } from './dom/task.js' ;
31+ import { flush_tasks , scheduler_yield } from './dom/task.js' ;
3232import { add_owner } from './dev/ownership.js' ;
3333import { internal_set , set , source } from './reactivity/sources.js' ;
3434import { destroy_derived , execute_derived , update_derived } from './reactivity/deriveds.js' ;
@@ -40,6 +40,7 @@ import { tracing_expressions, get_stack } from './dev/tracing.js';
4040
4141const FLUSH_MICROTASK = 0 ;
4242const FLUSH_SYNC = 1 ;
43+ const FLUSH_YIELD = 2 ;
4344// Used for DEV time error handling
4445/** @param {WeakSet<Error> } value */
4546const handled_errors = new WeakSet ( ) ;
@@ -49,6 +50,9 @@ export let is_throwing_error = false;
4950let scheduler_mode = FLUSH_MICROTASK ;
5051// Used for handling scheduling
5152let is_micro_task_queued = false ;
53+ let is_yield_queued = false ;
54+ /** @type {Array<() => void> } */
55+ let queued_yield_tasks = [ ] ;
5256
5357/** @type {Effect | null } */
5458let last_scheduled_effect = null ;
@@ -591,9 +595,10 @@ function infinite_loop_guard() {
591595
592596/**
593597 * @param {Array<Effect> } root_effects
598+ * @param {boolean } yielded_effects
594599 * @returns {void }
595600 */
596- function flush_queued_root_effects ( root_effects ) {
601+ function flush_queued_root_effects ( root_effects , yielded_effects ) {
597602 var length = root_effects . length ;
598603 if ( length === 0 ) {
599604 return ;
@@ -614,45 +619,14 @@ function flush_queued_root_effects(root_effects) {
614619 /** @type {Effect[] } */
615620 var collected_effects = [ ] ;
616621
617- process_effects ( effect , collected_effects ) ;
622+ process_effects ( effect , collected_effects , yielded_effects ) ;
618623 flush_queued_effects ( collected_effects ) ;
619624 }
620625 } finally {
621626 is_flushing_effect = previously_flushing_effect ;
622627 }
623628}
624629
625- /**
626- * @param {Effect } effect
627- * @returns {void }
628- */
629- function flush_effect ( effect ) {
630- if ( ( effect . f & ( DESTROYED | INERT ) ) === 0 ) {
631- try {
632- if ( check_dirtiness ( effect ) ) {
633- update_effect ( effect ) ;
634-
635- // Effects with no dependencies or teardown do not get added to the effect tree.
636- // Deferred effects (e.g. `$effect(...)`) _are_ added to the tree because we
637- // don't know if we need to keep them until they are executed. Doing the check
638- // here (rather than in `update_effect`) allows us to skip the work for
639- // immediate effects.
640- if ( effect . deps === null && effect . first === null && effect . nodes_start === null ) {
641- if ( effect . teardown === null ) {
642- // remove this effect from the graph
643- unlink_effect ( effect ) ;
644- } else {
645- // keep the effect in the graph, but free up some memory
646- effect . fn = null ;
647- }
648- }
649- }
650- } catch ( error ) {
651- handle_error ( error , effect , null , effect . ctx ) ;
652- }
653- }
654- }
655-
656630/**
657631 * @param {Array<Effect> } effects
658632 * @returns {void }
@@ -664,18 +638,41 @@ function flush_queued_effects(effects) {
664638 for ( var i = 0 ; i < length ; i ++ ) {
665639 var effect = effects [ i ] ;
666640
667- flush_effect ( effect ) ;
641+ if ( ( effect . f & ( DESTROYED | INERT ) ) === 0 ) {
642+ try {
643+ if ( check_dirtiness ( effect ) ) {
644+ update_effect ( effect ) ;
645+
646+ // Effects with no dependencies or teardown do not get added to the effect tree.
647+ // Deferred effects (e.g. `$effect(...)`) _are_ added to the tree because we
648+ // don't know if we need to keep them until they are executed. Doing the check
649+ // here (rather than in `update_effect`) allows us to skip the work for
650+ // immediate effects.
651+ if ( effect . deps === null && effect . first === null && effect . nodes_start === null ) {
652+ if ( effect . teardown === null ) {
653+ // remove this effect from the graph
654+ unlink_effect ( effect ) ;
655+ } else {
656+ // keep the effect in the graph, but free up some memory
657+ effect . fn = null ;
658+ }
659+ }
660+ }
661+ } catch ( error ) {
662+ handle_error ( error , effect , null , effect . ctx ) ;
663+ }
664+ }
668665 }
669666}
670667
671- function process_microtask_effects ( ) {
668+ function flush_effects ( yielded_effects = false ) {
672669 is_micro_task_queued = false ;
673670 if ( flush_count > 1001 ) {
674671 return ;
675672 }
676673 const previous_queued_root_effects = queued_root_effects ;
677674 queued_root_effects = [ ] ;
678- flush_queued_root_effects ( previous_queued_root_effects ) ;
675+ flush_queued_root_effects ( previous_queued_root_effects , yielded_effects ) ;
679676
680677 if ( ! is_micro_task_queued ) {
681678 flush_count = 0 ;
@@ -686,20 +683,44 @@ function process_microtask_effects() {
686683 }
687684}
688685
686+ function flush_yield_effects ( ) {
687+ is_yield_queued = false ;
688+ var tasks = queued_yield_tasks . slice ( ) ;
689+ queued_yield_tasks = [ ] ;
690+ var previous_scheduler_mode = scheduler_mode ;
691+ scheduler_mode = FLUSH_YIELD ;
692+ try {
693+ run_all ( tasks ) ;
694+ } finally {
695+ scheduler_mode = previous_scheduler_mode ;
696+ }
697+ flush_effects ( true ) ;
698+ }
699+
700+ /**
701+ * @param {() => void } fn
702+ */
703+ export function queue_yield ( fn ) {
704+ if ( ! is_yield_queued ) {
705+ is_yield_queued = true ;
706+ scheduler_yield ( flush_yield_effects ) ;
707+ }
708+ queued_yield_tasks . push ( fn ) ;
709+ }
710+
689711/**
690712 * @param {Effect } signal
691713 * @returns {void }
692714 */
693715export function schedule_effect ( signal ) {
694- if ( ( signal . f & YIELD_EFFECT ) !== 0 ) {
695- queue_yield_task ( ( ) => {
696- flush_effect ( signal ) ;
697- process_microtask_effects ( ) ;
716+ if ( scheduler_mode !== FLUSH_YIELD && ( signal . f & YIELD_EFFECT ) !== 0 ) {
717+ queue_yield ( ( ) => {
718+ schedule_effect ( signal ) ;
698719 } ) ;
699- } else if ( scheduler_mode === FLUSH_MICROTASK ) {
720+ } else if ( scheduler_mode !== FLUSH_SYNC ) {
700721 if ( ! is_micro_task_queued ) {
701722 is_micro_task_queued = true ;
702- queueMicrotask ( process_microtask_effects ) ;
723+ queueMicrotask ( flush_effects ) ;
703724 }
704725 }
705726
@@ -729,9 +750,10 @@ export function schedule_effect(signal) {
729750 *
730751 * @param {Effect } effect
731752 * @param {Effect[] } collected_effects
753+ * @param {boolean } yielded_effects
732754 * @returns {void }
733755 */
734- function process_effects ( effect , collected_effects ) {
756+ function process_effects ( effect , collected_effects , yielded_effects ) {
735757 var current_effect = effect . first ;
736758 var effects = [ ] ;
737759
@@ -761,7 +783,7 @@ function process_effects(effect, collected_effects) {
761783 current_effect = child ;
762784 continue ;
763785 }
764- } else if ( ( flags & EFFECT ) !== 0 ) {
786+ } else if ( ( flags & EFFECT ) !== 0 || ( yielded_effects && ( flags & YIELD_EFFECT ) !== 0 ) ) {
765787 effects . push ( current_effect ) ;
766788 }
767789 }
@@ -790,7 +812,7 @@ function process_effects(effect, collected_effects) {
790812 for ( var i = 0 ; i < effects . length ; i ++ ) {
791813 child = effects [ i ] ;
792814 collected_effects . push ( child ) ;
793- process_effects ( child , collected_effects ) ;
815+ process_effects ( child , collected_effects , yielded_effects ) ;
794816 }
795817}
796818
@@ -814,7 +836,7 @@ export function flush_sync(fn) {
814836 queued_root_effects = root_effects ;
815837 is_micro_task_queued = false ;
816838
817- flush_queued_root_effects ( previous_queued_root_effects ) ;
839+ flush_queued_root_effects ( previous_queued_root_effects , true ) ;
818840
819841 var result = fn ?. ( ) ;
820842
0 commit comments