@@ -10,7 +10,8 @@ import {
1010 INERT ,
1111 RENDER_EFFECT ,
1212 ROOT_EFFECT ,
13- USER_EFFECT
13+ USER_EFFECT ,
14+ MAYBE_DIRTY
1415} from '#client/constants' ;
1516import { async_mode_flag } from '../../flags/index.js' ;
1617import { deferred , define_property } from '../../shared/utils.js' ;
@@ -27,7 +28,7 @@ import * as e from '../errors.js';
2728import { flush_tasks } from '../dom/task.js' ;
2829import { DEV } from 'esm-env' ;
2930import { invoke_error_boundary } from '../error-handling.js' ;
30- import { mark_reactions , old_values } from './sources.js' ;
31+ import { old_values } from './sources.js' ;
3132import { unlink_effect } from './effects.js' ;
3233import { unset_context } from './async.js' ;
3334
@@ -146,6 +147,18 @@ export class Batch {
146147 */
147148 #block_effects = [ ] ;
148149
150+ /**
151+ * Deferred effects (which run after async work has completed) that are DIRTY
152+ * @type {Effect[] }
153+ */
154+ #dirty_effects = [ ] ;
155+
156+ /**
157+ * Deferred effects that are MAYBE_DIRTY
158+ * @type {Effect[] }
159+ */
160+ #maybe_dirty_effects = [ ] ;
161+
149162 /**
150163 * A set of branches that still exist, but will be destroyed when this batch
151164 * is committed — we skip over these during `process`
@@ -221,10 +234,9 @@ export class Batch {
221234
222235 this . #deferred?. resolve ( ) ;
223236 } else {
224- // otherwise mark effects clean so they get scheduled on the next run
225- for ( const e of this . #render_effects) set_signal_status ( e , CLEAN ) ;
226- for ( const e of this . #effects) set_signal_status ( e , CLEAN ) ;
227- for ( const e of this . #block_effects) set_signal_status ( e , CLEAN ) ;
237+ this . #defer_effects( this . #render_effects) ;
238+ this . #defer_effects( this . #effects) ;
239+ this . #defer_effects( this . #block_effects) ;
228240 }
229241
230242 if ( current_values ) {
@@ -271,15 +283,15 @@ export class Batch {
271283 if ( ! skip && effect . fn !== null ) {
272284 if ( is_branch ) {
273285 effect . f ^= CLEAN ;
274- } else if ( ( flags & EFFECT ) ! == 0 ) {
275- this . #effects . push ( effect ) ;
276- } else if ( async_mode_flag && ( flags & RENDER_EFFECT ) !== 0 ) {
277- this . #render_effects . push ( effect ) ;
278- } else if ( is_dirty ( effect ) ) {
279- if ( ( flags & ASYNC ) !== 0 ) {
286+ } else if ( ( flags & CLEAN ) = == 0 ) {
287+ if ( ( flags & EFFECT ) !== 0 ) {
288+ this . #effects . push ( effect ) ;
289+ } else if ( async_mode_flag && ( flags & RENDER_EFFECT ) !== 0 ) {
290+ this . #render_effects . push ( effect ) ;
291+ } else if ( ( flags & ASYNC ) !== 0 ) {
280292 var effects = effect . b ?. pending ? this . #boundary_async_effects : this . #async_effects;
281293 effects . push ( effect ) ;
282- } else {
294+ } else if ( is_dirty ( effect ) ) {
283295 if ( ( effect . f & BLOCK_EFFECT ) !== 0 ) this . #block_effects. push ( effect ) ;
284296 update_effect ( effect ) ;
285297 }
@@ -303,6 +315,21 @@ export class Batch {
303315 }
304316 }
305317
318+ /**
319+ * @param {Effect[] } effects
320+ */
321+ #defer_effects( effects ) {
322+ for ( const e of effects ) {
323+ const target = ( e . f & DIRTY ) !== 0 ? this . #dirty_effects : this . #maybe_dirty_effects;
324+ target . push ( e ) ;
325+
326+ // mark as clean so they get scheduled if they depend on pending async state
327+ set_signal_status ( e , CLEAN ) ;
328+ }
329+
330+ effects . length = 0 ;
331+ }
332+
306333 /**
307334 * Associate a change to a given source with the current
308335 * batch, noting its previous and current values
@@ -380,8 +407,14 @@ export class Batch {
380407 this . #pending -= 1 ;
381408
382409 if ( this . #pending === 0 ) {
383- for ( const source of this . current . keys ( ) ) {
384- mark_reactions ( source , DIRTY , false ) ;
410+ for ( const e of this . #dirty_effects) {
411+ set_signal_status ( e , DIRTY ) ;
412+ schedule_effect ( e ) ;
413+ }
414+
415+ for ( const e of this . #maybe_dirty_effects) {
416+ set_signal_status ( e , MAYBE_DIRTY ) ;
417+ schedule_effect ( e ) ;
385418 }
386419
387420 this . #render_effects = [ ] ;
0 commit comments